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;
42 typedef struct DSoundRenderImpl
44 struct strmbase_renderer renderer
;
46 IBasicAudio IBasicAudio_iface
;
47 IAMDirectSound IAMDirectSound_iface
;
48 IUnknown
*system_clock
;
50 IDirectSound8
*dsound
;
51 LPDIRECTSOUNDBUFFER dsbuffer
;
54 DWORD last_playpos
, writepos
;
56 REFERENCE_TIME play_time
;
62 static inline DSoundRenderImpl
*impl_from_strmbase_renderer(struct strmbase_renderer
*iface
)
64 return CONTAINING_RECORD(iface
, DSoundRenderImpl
, renderer
);
67 static inline DSoundRenderImpl
*impl_from_IBasicAudio(IBasicAudio
*iface
)
69 return CONTAINING_RECORD(iface
, DSoundRenderImpl
, IBasicAudio_iface
);
72 static inline DSoundRenderImpl
*impl_from_IAMDirectSound(IAMDirectSound
*iface
)
74 return CONTAINING_RECORD(iface
, DSoundRenderImpl
, IAMDirectSound_iface
);
77 static REFERENCE_TIME
time_from_pos(DSoundRenderImpl
*This
, DWORD pos
) {
78 WAVEFORMATEX
*wfx
= (WAVEFORMATEX
*)This
->renderer
.sink
.pin
.mt
.pbFormat
;
79 REFERENCE_TIME ret
= 10000000;
80 ret
= ret
* pos
/ wfx
->nAvgBytesPerSec
;
84 static DWORD
pos_from_time(DSoundRenderImpl
*This
, REFERENCE_TIME time
) {
85 WAVEFORMATEX
*wfx
= (WAVEFORMATEX
*)This
->renderer
.sink
.pin
.mt
.pbFormat
;
86 REFERENCE_TIME ret
= time
;
87 ret
*= wfx
->nAvgBytesPerSec
;
89 ret
-= ret
% wfx
->nBlockAlign
;
93 static void DSoundRender_UpdatePositions(DSoundRenderImpl
*This
, DWORD
*seqwritepos
, DWORD
*minwritepos
) {
94 WAVEFORMATEX
*wfx
= (WAVEFORMATEX
*)This
->renderer
.sink
.pin
.mt
.pbFormat
;
96 DWORD size1
, size2
, playpos
, writepos
, old_writepos
, old_playpos
, adv
;
97 BOOL writepos_set
= This
->writepos
< This
->buf_size
;
99 /* Update position and zero */
100 old_writepos
= This
->writepos
;
101 old_playpos
= This
->last_playpos
;
102 if (old_writepos
<= old_playpos
)
103 old_writepos
+= This
->buf_size
;
105 IDirectSoundBuffer_GetCurrentPosition(This
->dsbuffer
, &playpos
, &writepos
);
106 if (old_playpos
> playpos
) {
107 adv
= This
->buf_size
+ playpos
- old_playpos
;
108 This
->play_time
+= time_from_pos(This
, This
->buf_size
);
110 adv
= playpos
- old_playpos
;
111 This
->last_playpos
= playpos
;
113 TRACE("Moving from %u to %u: clearing %u bytes\n", old_playpos
, playpos
, adv
);
114 IDirectSoundBuffer_Lock(This
->dsbuffer
, old_playpos
, adv
, (void**)&buf1
, &size1
, (void**)&buf2
, &size2
, 0);
115 memset(buf1
, wfx
->wBitsPerSample
== 8 ? 128 : 0, size1
);
116 memset(buf2
, wfx
->wBitsPerSample
== 8 ? 128 : 0, size2
);
117 IDirectSoundBuffer_Unlock(This
->dsbuffer
, buf1
, size1
, buf2
, size2
);
119 *minwritepos
= writepos
;
120 if (!writepos_set
|| old_writepos
< writepos
) {
122 This
->writepos
= This
->buf_size
;
123 FIXME("Underrun of data occurred!\n");
125 *seqwritepos
= writepos
;
127 *seqwritepos
= This
->writepos
;
130 static HRESULT
DSoundRender_GetWritePos(DSoundRenderImpl
*This
, DWORD
*ret_writepos
, REFERENCE_TIME write_at
, DWORD
*pfree
, DWORD
*skip
)
132 WAVEFORMATEX
*wfx
= (WAVEFORMATEX
*)This
->renderer
.sink
.pin
.mt
.pbFormat
;
133 DWORD writepos
, min_writepos
, playpos
;
134 REFERENCE_TIME max_lag
= 50 * 10000;
135 REFERENCE_TIME cur
, writepos_t
, delta_t
;
137 DSoundRender_UpdatePositions(This
, &writepos
, &min_writepos
);
138 playpos
= This
->last_playpos
;
139 if (This
->renderer
.filter
.clock
)
141 IReferenceClock_GetTime(This
->renderer
.filter
.clock
, &cur
);
142 cur
-= This
->renderer
.stream_start
;
146 if (writepos
== min_writepos
)
151 *ret_writepos
= writepos
;
155 if (writepos
>= playpos
)
156 writepos_t
= cur
+ time_from_pos(This
, writepos
- playpos
);
158 writepos_t
= cur
+ time_from_pos(This
, This
->buf_size
+ writepos
- playpos
);
160 /* write_at: Starting time of sample */
161 /* cur: current time of play position */
162 /* writepos_t: current time of our pointer play position */
163 delta_t
= write_at
- writepos_t
;
164 if (delta_t
>= -max_lag
&& delta_t
<= max_lag
) {
165 TRACE("Continuing from old position\n");
166 *ret_writepos
= writepos
;
167 } else if (delta_t
< 0) {
168 REFERENCE_TIME past
, min_writepos_t
;
169 WARN("Delta too big %s/%s, overwriting old data or even skipping\n", debugstr_time(delta_t
), debugstr_time(max_lag
));
170 if (min_writepos
>= playpos
)
171 min_writepos_t
= cur
+ time_from_pos(This
, min_writepos
- playpos
);
173 min_writepos_t
= cur
+ time_from_pos(This
, This
->buf_size
- playpos
+ min_writepos
);
174 past
= min_writepos_t
- write_at
;
176 DWORD skipbytes
= pos_from_time(This
, past
);
177 WARN("Skipping %u bytes\n", skipbytes
);
179 *ret_writepos
= min_writepos
;
181 DWORD aheadbytes
= pos_from_time(This
, -past
);
182 WARN("Advancing %u bytes\n", aheadbytes
);
183 *ret_writepos
= (min_writepos
+ aheadbytes
) % This
->buf_size
;
185 } else /* delta_t > 0 */ {
187 WARN("Delta too big %s/%s, too far ahead\n", debugstr_time(delta_t
), debugstr_time(max_lag
));
188 aheadbytes
= pos_from_time(This
, delta_t
);
189 WARN("Advancing %u bytes\n", aheadbytes
);
190 if (delta_t
>= DSoundRenderer_Max_Fill
)
192 *ret_writepos
= (min_writepos
+ aheadbytes
) % This
->buf_size
;
195 if (playpos
> *ret_writepos
)
196 *pfree
= playpos
- *ret_writepos
;
197 else if (playpos
== *ret_writepos
)
198 *pfree
= This
->buf_size
- wfx
->nBlockAlign
;
200 *pfree
= This
->buf_size
+ playpos
- *ret_writepos
;
201 if (time_from_pos(This
, This
->buf_size
- *pfree
) >= DSoundRenderer_Max_Fill
) {
202 TRACE("Blocked: too full %s / %s\n", debugstr_time(time_from_pos(This
, This
->buf_size
- *pfree
)),
203 debugstr_time(DSoundRenderer_Max_Fill
));
209 static HRESULT
DSoundRender_HandleEndOfStream(DSoundRenderImpl
*This
)
211 while (This
->renderer
.filter
.state
== State_Running
)
214 DSoundRender_UpdatePositions(This
, &pos1
, &pos2
);
219 LeaveCriticalSection(&This
->renderer
.csRenderLock
);
220 WaitForSingleObject(This
->renderer
.flush_event
, 10);
221 EnterCriticalSection(&This
->renderer
.csRenderLock
);
228 static HRESULT
DSoundRender_SendSampleData(DSoundRenderImpl
* This
, REFERENCE_TIME tStart
, REFERENCE_TIME tStop
, const BYTE
*data
, DWORD size
)
232 while (size
&& This
->renderer
.filter
.state
!= State_Stopped
) {
233 DWORD writepos
, skip
= 0, free
, size1
, size2
, ret
;
236 if (This
->renderer
.filter
.state
== State_Running
)
237 hr
= DSoundRender_GetWritePos(This
, &writepos
, tStart
, &free
, &skip
);
243 LeaveCriticalSection(&This
->renderer
.csRenderLock
);
244 ret
= WaitForSingleObject(This
->renderer
.flush_event
, 10);
245 EnterCriticalSection(&This
->renderer
.csRenderLock
);
247 if (This
->renderer
.sink
.flushing
|| This
->renderer
.filter
.state
== State_Stopped
)
248 return This
->renderer
.filter
.state
== State_Paused
? S_OK
: VFW_E_WRONG_STATE
;
249 if (ret
!= WAIT_TIMEOUT
)
256 FIXME("Sample dropped %u of %u bytes\n", skip
, size
);
262 hr
= IDirectSoundBuffer_Lock(This
->dsbuffer
, writepos
, min(free
, size
), (void**)&buf1
, &size1
, (void**)&buf2
, &size2
, 0);
264 ERR("Unable to lock sound buffer! (%x)\n", hr
);
267 memcpy(buf1
, data
, size1
);
269 memcpy(buf2
, data
+size1
, size2
);
270 IDirectSoundBuffer_Unlock(This
->dsbuffer
, buf1
, size1
, buf2
, size2
);
271 This
->writepos
= (writepos
+ size1
+ size2
) % This
->buf_size
;
272 TRACE("Wrote %u bytes at %u, next at %u - (%u/%u)\n", size1
+size2
, writepos
, This
->writepos
, free
, size
);
273 data
+= size1
+ size2
;
274 size
-= size1
+ size2
;
279 static HRESULT WINAPI
DSoundRender_ShouldDrawSampleNow(struct strmbase_renderer
*iface
,
280 IMediaSample
*sample
, REFERENCE_TIME
*start
, REFERENCE_TIME
*end
)
282 /* We time ourselves do not use the base renderers timing */
287 static HRESULT WINAPI
DSoundRender_PrepareReceive(struct strmbase_renderer
*iface
, IMediaSample
*pSample
)
289 DSoundRenderImpl
*This
= impl_from_strmbase_renderer(iface
);
293 if (IMediaSample_GetMediaType(pSample
, &amt
) == S_OK
)
295 AM_MEDIA_TYPE
*orig
= &This
->renderer
.sink
.pin
.mt
;
296 WAVEFORMATEX
*origfmt
= (WAVEFORMATEX
*)orig
->pbFormat
;
297 WAVEFORMATEX
*newfmt
= (WAVEFORMATEX
*)amt
->pbFormat
;
299 if (origfmt
->wFormatTag
== newfmt
->wFormatTag
&&
300 origfmt
->nChannels
== newfmt
->nChannels
&&
301 origfmt
->nBlockAlign
== newfmt
->nBlockAlign
&&
302 origfmt
->wBitsPerSample
== newfmt
->wBitsPerSample
&&
303 origfmt
->cbSize
== newfmt
->cbSize
)
305 if (origfmt
->nSamplesPerSec
!= newfmt
->nSamplesPerSec
)
307 hr
= IDirectSoundBuffer_SetFrequency(This
->dsbuffer
,
308 newfmt
->nSamplesPerSec
);
310 return VFW_E_TYPE_NOT_ACCEPTED
;
312 CopyMediaType(orig
, amt
);
313 IMediaSample_SetMediaType(pSample
, NULL
);
317 return VFW_E_TYPE_NOT_ACCEPTED
;
322 static HRESULT WINAPI
DSoundRender_DoRenderSample(struct strmbase_renderer
*iface
, IMediaSample
*pSample
)
324 DSoundRenderImpl
*This
= impl_from_strmbase_renderer(iface
);
325 LPBYTE pbSrcStream
= NULL
;
326 LONG cbSrcStream
= 0;
327 REFERENCE_TIME tStart
, tStop
;
330 TRACE("%p %p\n", iface
, pSample
);
332 /* Slightly incorrect, Pause completes when a frame is received so we should signal
333 * pause completion here, but for sound playing a single frame doesn't make sense
336 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
339 ERR("Cannot get pointer to sample data (%x)\n", hr
);
343 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
345 ERR("Cannot get sample time (%x)\n", hr
);
349 IMediaSample_IsDiscontinuity(pSample
);
351 if (IMediaSample_IsPreroll(pSample
) == S_OK
)
357 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
358 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream
, cbSrcStream
);
360 hr
= DSoundRender_SendSampleData(This
, tStart
, tStop
, pbSrcStream
, cbSrcStream
);
361 if (This
->renderer
.filter
.state
== State_Running
&& This
->renderer
.filter
.clock
&& tStart
>= 0) {
362 REFERENCE_TIME jitter
, now
= 0;
364 IReferenceClock_GetTime(This
->renderer
.filter
.clock
, &now
);
365 jitter
= now
- This
->renderer
.stream_start
- tStart
;
366 if (jitter
<= -DSoundRenderer_Max_Fill
)
367 jitter
+= DSoundRenderer_Max_Fill
;
370 q
.Type
= (jitter
> 0 ? Famine
: Flood
);
373 q
.TimeStamp
= tStart
;
374 IQualityControl_Notify((IQualityControl
*)This
->renderer
.qcimpl
, &This
->renderer
.filter
.IBaseFilter_iface
, q
);
379 static HRESULT WINAPI
DSoundRender_CheckMediaType(struct strmbase_renderer
*iface
, const AM_MEDIA_TYPE
* pmt
)
381 if (!IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Audio
))
384 if (!IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_PCM
))
390 static void dsound_render_stop_stream(struct strmbase_renderer
*iface
)
392 DSoundRenderImpl
*This
= impl_from_strmbase_renderer(iface
);
394 TRACE("(%p/%p)->()\n", This
, iface
);
396 IDirectSoundBuffer_Stop(This
->dsbuffer
);
397 This
->writepos
= This
->buf_size
;
400 static void dsound_render_start_stream(struct strmbase_renderer
*iface
)
402 DSoundRenderImpl
*This
= impl_from_strmbase_renderer(iface
);
404 TRACE("(%p)\n", This
);
406 if (This
->renderer
.sink
.pin
.peer
)
408 IDirectSoundBuffer_Play(This
->dsbuffer
, 0, 0, DSBPLAY_LOOPING
);
412 static HRESULT
dsound_render_connect(struct strmbase_renderer
*iface
, const AM_MEDIA_TYPE
*mt
)
414 DSoundRenderImpl
*This
= impl_from_strmbase_renderer(iface
);
415 const WAVEFORMATEX
*format
= (WAVEFORMATEX
*)mt
->pbFormat
;
417 DSBUFFERDESC buf_desc
;
419 This
->buf_size
= format
->nAvgBytesPerSec
;
421 memset(&buf_desc
,0,sizeof(DSBUFFERDESC
));
422 buf_desc
.dwSize
= sizeof(DSBUFFERDESC
);
423 buf_desc
.dwFlags
= DSBCAPS_CTRLVOLUME
| DSBCAPS_CTRLPAN
|
424 DSBCAPS_CTRLFREQUENCY
| DSBCAPS_GLOBALFOCUS
|
425 DSBCAPS_GETCURRENTPOSITION2
;
426 buf_desc
.dwBufferBytes
= This
->buf_size
;
427 buf_desc
.lpwfxFormat
= (WAVEFORMATEX
*)format
;
428 hr
= IDirectSound8_CreateSoundBuffer(This
->dsound
, &buf_desc
, &This
->dsbuffer
, NULL
);
429 This
->writepos
= This
->buf_size
;
431 ERR("Can't create sound buffer (%x)\n", hr
);
435 hr
= IDirectSoundBuffer_SetVolume(This
->dsbuffer
, This
->volume
);
437 ERR("Can't set volume to %d (%x)\n", This
->volume
, hr
);
439 hr
= IDirectSoundBuffer_SetPan(This
->dsbuffer
, This
->pan
);
441 ERR("Can't set pan to %d (%x)\n", This
->pan
, hr
);
445 if (FAILED(hr
) && hr
!= VFW_E_ALREADY_CONNECTED
)
448 IDirectSoundBuffer_Release(This
->dsbuffer
);
449 This
->dsbuffer
= NULL
;
455 static HRESULT WINAPI
DSoundRender_BreakConnect(struct strmbase_renderer
*iface
)
457 DSoundRenderImpl
*This
= impl_from_strmbase_renderer(iface
);
459 TRACE("(%p)->()\n", iface
);
462 IDirectSoundBuffer_Release(This
->dsbuffer
);
463 This
->dsbuffer
= NULL
;
468 static HRESULT WINAPI
DSoundRender_EndOfStream(struct strmbase_renderer
*iface
)
470 DSoundRenderImpl
*This
= impl_from_strmbase_renderer(iface
);
471 return DSoundRender_HandleEndOfStream(This
);
474 static HRESULT WINAPI
DSoundRender_EndFlush(struct strmbase_renderer
*iface
)
476 DSoundRenderImpl
*This
= impl_from_strmbase_renderer(iface
);
484 IDirectSoundBuffer_Lock(This
->dsbuffer
, 0, 0, (LPVOID
*)&buffer
, &size
, NULL
, NULL
, DSBLOCK_ENTIREBUFFER
);
485 memset(buffer
, 0, size
);
486 IDirectSoundBuffer_Unlock(This
->dsbuffer
, buffer
, size
, NULL
, 0);
487 This
->writepos
= This
->buf_size
;
493 static void dsound_render_destroy(struct strmbase_renderer
*iface
)
495 DSoundRenderImpl
*filter
= impl_from_strmbase_renderer(iface
);
497 if (filter
->dsbuffer
)
498 IDirectSoundBuffer_Release(filter
->dsbuffer
);
499 filter
->dsbuffer
= NULL
;
501 IDirectSound8_Release(filter
->dsound
);
502 filter
->dsound
= NULL
;
504 strmbase_renderer_cleanup(&filter
->renderer
);
505 CoTaskMemFree(filter
);
508 static HRESULT
dsound_render_query_interface(struct strmbase_renderer
*iface
, REFIID iid
, void **out
)
510 DSoundRenderImpl
*filter
= impl_from_strmbase_renderer(iface
);
512 if (IsEqualGUID(iid
, &IID_IBasicAudio
))
513 *out
= &filter
->IBasicAudio_iface
;
514 else if (IsEqualGUID(iid
, &IID_IReferenceClock
))
515 return IUnknown_QueryInterface(filter
->system_clock
, iid
, out
);
516 else if (IsEqualGUID(iid
, &IID_IAMDirectSound
))
517 *out
= &filter
->IAMDirectSound_iface
;
519 return E_NOINTERFACE
;
521 IUnknown_AddRef((IUnknown
*)*out
);
525 static const struct strmbase_renderer_ops renderer_ops
=
527 .pfnCheckMediaType
= DSoundRender_CheckMediaType
,
528 .pfnDoRenderSample
= DSoundRender_DoRenderSample
,
529 .renderer_start_stream
= dsound_render_start_stream
,
530 .renderer_stop_stream
= dsound_render_stop_stream
,
531 .pfnShouldDrawSampleNow
= DSoundRender_ShouldDrawSampleNow
,
532 .pfnPrepareReceive
= DSoundRender_PrepareReceive
,
533 .renderer_connect
= dsound_render_connect
,
534 .pfnBreakConnect
= DSoundRender_BreakConnect
,
535 .pfnEndOfStream
= DSoundRender_EndOfStream
,
536 .pfnEndFlush
= DSoundRender_EndFlush
,
537 .renderer_destroy
= dsound_render_destroy
,
538 .renderer_query_interface
= dsound_render_query_interface
,
541 /*** IUnknown methods ***/
542 static HRESULT WINAPI
Basicaudio_QueryInterface(IBasicAudio
*iface
,
545 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
547 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppvObj
);
549 return IUnknown_QueryInterface(This
->renderer
.filter
.outer_unk
, riid
, ppvObj
);
552 static ULONG WINAPI
Basicaudio_AddRef(IBasicAudio
*iface
) {
553 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
555 TRACE("(%p/%p)->()\n", This
, iface
);
557 return IUnknown_AddRef(This
->renderer
.filter
.outer_unk
);
560 static ULONG WINAPI
Basicaudio_Release(IBasicAudio
*iface
) {
561 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
563 TRACE("(%p/%p)->()\n", This
, iface
);
565 return IUnknown_Release(This
->renderer
.filter
.outer_unk
);
568 HRESULT WINAPI
basic_audio_GetTypeInfoCount(IBasicAudio
*iface
, UINT
*count
)
570 TRACE("iface %p, count %p.\n", iface
, count
);
575 HRESULT WINAPI
basic_audio_GetTypeInfo(IBasicAudio
*iface
, UINT index
,
576 LCID lcid
, ITypeInfo
**typeinfo
)
578 TRACE("iface %p, index %u, lcid %#x, typeinfo %p.\n", iface
, index
, lcid
, typeinfo
);
579 return strmbase_get_typeinfo(IBasicAudio_tid
, typeinfo
);
582 HRESULT WINAPI
basic_audio_GetIDsOfNames(IBasicAudio
*iface
, REFIID iid
,
583 LPOLESTR
*names
, UINT count
, LCID lcid
, DISPID
*ids
)
588 TRACE("iface %p, iid %s, names %p, count %u, lcid %#x, ids %p.\n",
589 iface
, debugstr_guid(iid
), names
, count
, lcid
, ids
);
591 if (SUCCEEDED(hr
= strmbase_get_typeinfo(IBasicAudio_tid
, &typeinfo
)))
593 hr
= ITypeInfo_GetIDsOfNames(typeinfo
, names
, count
, ids
);
594 ITypeInfo_Release(typeinfo
);
599 static HRESULT WINAPI
basic_audio_Invoke(IBasicAudio
*iface
, DISPID id
, REFIID iid
, LCID lcid
,
600 WORD flags
, DISPPARAMS
*params
, VARIANT
*result
, EXCEPINFO
*excepinfo
, UINT
*error_arg
)
605 TRACE("iface %p, id %d, iid %s, lcid %#x, flags %#x, params %p, result %p, excepinfo %p, error_arg %p.\n",
606 iface
, id
, debugstr_guid(iid
), lcid
, flags
, params
, result
, excepinfo
, error_arg
);
608 if (SUCCEEDED(hr
= strmbase_get_typeinfo(IBasicAudio_tid
, &typeinfo
)))
610 hr
= ITypeInfo_Invoke(typeinfo
, iface
, id
, flags
, params
, result
, excepinfo
, error_arg
);
611 ITypeInfo_Release(typeinfo
);
616 static HRESULT WINAPI
Basicaudio_put_Volume(IBasicAudio
*iface
,
618 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
620 TRACE("(%p/%p)->(%d)\n", This
, iface
, lVolume
);
622 if (lVolume
> DSBVOLUME_MAX
|| lVolume
< DSBVOLUME_MIN
)
625 if (This
->dsbuffer
) {
626 if (FAILED(IDirectSoundBuffer_SetVolume(This
->dsbuffer
, lVolume
)))
630 This
->volume
= lVolume
;
634 static HRESULT WINAPI
Basicaudio_get_Volume(IBasicAudio
*iface
,
636 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
638 TRACE("(%p/%p)->(%p)\n", This
, iface
, plVolume
);
643 *plVolume
= This
->volume
;
647 static HRESULT WINAPI
Basicaudio_put_Balance(IBasicAudio
*iface
,
649 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
651 TRACE("(%p/%p)->(%d)\n", This
, iface
, lBalance
);
653 if (lBalance
< DSBPAN_LEFT
|| lBalance
> DSBPAN_RIGHT
)
656 if (This
->dsbuffer
) {
657 if (FAILED(IDirectSoundBuffer_SetPan(This
->dsbuffer
, lBalance
)))
661 This
->pan
= lBalance
;
665 static HRESULT WINAPI
Basicaudio_get_Balance(IBasicAudio
*iface
,
667 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
669 TRACE("(%p/%p)->(%p)\n", This
, iface
, plBalance
);
674 *plBalance
= This
->pan
;
678 static const IBasicAudioVtbl IBasicAudio_Vtbl
=
680 Basicaudio_QueryInterface
,
683 basic_audio_GetTypeInfoCount
,
684 basic_audio_GetTypeInfo
,
685 basic_audio_GetIDsOfNames
,
687 Basicaudio_put_Volume
,
688 Basicaudio_get_Volume
,
689 Basicaudio_put_Balance
,
690 Basicaudio_get_Balance
693 /*** IUnknown methods ***/
694 static HRESULT WINAPI
AMDirectSound_QueryInterface(IAMDirectSound
*iface
,
698 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
700 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppvObj
);
702 return IUnknown_QueryInterface(This
->renderer
.filter
.outer_unk
, riid
, ppvObj
);
705 static ULONG WINAPI
AMDirectSound_AddRef(IAMDirectSound
*iface
)
707 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
709 TRACE("(%p/%p)->()\n", This
, iface
);
711 return IUnknown_AddRef(This
->renderer
.filter
.outer_unk
);
714 static ULONG WINAPI
AMDirectSound_Release(IAMDirectSound
*iface
)
716 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
718 TRACE("(%p/%p)->()\n", This
, iface
);
720 return IUnknown_Release(This
->renderer
.filter
.outer_unk
);
723 /*** IAMDirectSound methods ***/
724 static HRESULT WINAPI
AMDirectSound_GetDirectSoundInterface(IAMDirectSound
*iface
, IDirectSound
**ds
)
726 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
728 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, ds
);
733 static HRESULT WINAPI
AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
**buf
)
735 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
737 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
742 static HRESULT WINAPI
AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
**buf
)
744 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
746 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
751 static HRESULT WINAPI
AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound
*iface
, IDirectSound
*ds
)
753 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
755 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, ds
);
760 static HRESULT WINAPI
AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
*buf
)
762 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
764 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
769 static HRESULT WINAPI
AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
*buf
)
771 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
773 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
778 static HRESULT WINAPI
AMDirectSound_SetFocusWindow(IAMDirectSound
*iface
, HWND hwnd
, BOOL bgaudible
)
780 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
782 FIXME("(%p/%p)->(%p,%d): stub\n", This
, iface
, hwnd
, bgaudible
);
787 static HRESULT WINAPI
AMDirectSound_GetFocusWindow(IAMDirectSound
*iface
, HWND
*hwnd
, BOOL
*bgaudible
)
789 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
791 FIXME("(%p/%p)->(%p,%p): stub\n", This
, iface
, hwnd
, bgaudible
);
796 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl
=
798 AMDirectSound_QueryInterface
,
799 AMDirectSound_AddRef
,
800 AMDirectSound_Release
,
801 AMDirectSound_GetDirectSoundInterface
,
802 AMDirectSound_GetPrimaryBufferInterface
,
803 AMDirectSound_GetSecondaryBufferInterface
,
804 AMDirectSound_ReleaseDirectSoundInterface
,
805 AMDirectSound_ReleasePrimaryBufferInterface
,
806 AMDirectSound_ReleaseSecondaryBufferInterface
,
807 AMDirectSound_SetFocusWindow
,
808 AMDirectSound_GetFocusWindow
811 HRESULT
dsound_render_create(IUnknown
*outer
, void **out
)
813 static const DSBUFFERDESC buffer_desc
= {
814 .dwSize
= sizeof(DSBUFFERDESC
),
815 .dwFlags
= DSBCAPS_PRIMARYBUFFER
,
818 IDirectSoundBuffer
*buffer
;
819 DSoundRenderImpl
*object
;
822 if (!(object
= CoTaskMemAlloc(sizeof(*object
))))
823 return E_OUTOFMEMORY
;
824 memset(object
, 0, sizeof(*object
));
826 if (FAILED(hr
= strmbase_renderer_init(&object
->renderer
, outer
,
827 &CLSID_DSoundRender
, L
"Audio Input pin (rendered)", &renderer_ops
)))
829 CoTaskMemFree(object
);
833 if (FAILED(hr
= QUARTZ_CreateSystemClock(&object
->renderer
.filter
.IUnknown_inner
,
834 (void **)&object
->system_clock
)))
836 strmbase_renderer_cleanup(&object
->renderer
);
837 CoTaskMemFree(object
);
841 object
->IBasicAudio_iface
.lpVtbl
= &IBasicAudio_Vtbl
;
842 object
->IAMDirectSound_iface
.lpVtbl
= &IAMDirectSound_Vtbl
;
844 if (FAILED(hr
= DirectSoundCreate8(NULL
, &object
->dsound
, NULL
)))
846 IUnknown_Release(object
->system_clock
);
847 strmbase_renderer_cleanup(&object
->renderer
);
848 CoTaskMemFree(object
);
852 if (FAILED(hr
= IDirectSound8_SetCooperativeLevel(object
->dsound
,
853 GetDesktopWindow(), DSSCL_PRIORITY
)))
855 IDirectSound8_Release(object
->dsound
);
856 IUnknown_Release(object
->system_clock
);
857 strmbase_renderer_cleanup(&object
->renderer
);
858 CoTaskMemFree(object
);
862 if (SUCCEEDED(hr
= IDirectSound8_CreateSoundBuffer(object
->dsound
,
863 &buffer_desc
, &buffer
, NULL
)))
865 IDirectSoundBuffer_Play(buffer
, 0, 0, DSBPLAY_LOOPING
);
866 IDirectSoundBuffer_Release(buffer
);
869 TRACE("Created DirectSound renderer %p.\n", object
);
870 *out
= &object
->renderer
.filter
.IUnknown_inner
;