iphlpapi: Remove unnecessary memcpy from build_udp6_table.
[wine.git] / dlls / quartz / dsoundrender.c
blobe65363dea447abfe788e82e1ed8102b3420bc6e6
1 /*
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 #include "uuids.h"
24 #include "vfwmsgs.h"
25 #include "windef.h"
26 #include "winbase.h"
27 #include "dshow.h"
28 #include "evcode.h"
29 #include "strmif.h"
30 #include "dsound.h"
31 #include "amaudio.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;
52 DWORD buf_size;
53 DWORD in_loop;
54 DWORD last_playpos, writepos;
56 REFERENCE_TIME play_time;
58 LONG volume;
59 LONG pan;
60 } DSoundRenderImpl;
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;
81 return ret;
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;
88 ret /= 10000000;
89 ret -= ret % wfx->nBlockAlign;
90 return ret;
93 static void DSoundRender_UpdatePositions(DSoundRenderImpl *This, DWORD *seqwritepos, DWORD *minwritepos) {
94 WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
95 BYTE *buf1, *buf2;
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);
109 } else
110 adv = playpos - old_playpos;
111 This->last_playpos = playpos;
112 if (adv) {
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) {
121 if (writepos_set) {
122 This->writepos = This->buf_size;
123 FIXME("Underrun of data occurred!\n");
125 *seqwritepos = writepos;
126 } else
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;
143 } else
144 write_at = -1;
146 if (writepos == min_writepos)
147 max_lag = 0;
149 *skip = 0;
150 if (write_at < 0) {
151 *ret_writepos = writepos;
152 goto end;
155 if (writepos >= playpos)
156 writepos_t = cur + time_from_pos(This, writepos - playpos);
157 else
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);
172 else
173 min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos);
174 past = min_writepos_t - write_at;
175 if (past >= 0) {
176 DWORD skipbytes = pos_from_time(This, past);
177 WARN("Skipping %u bytes\n", skipbytes);
178 *skip = skipbytes;
179 *ret_writepos = min_writepos;
180 } else {
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 */ {
186 DWORD aheadbytes;
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)
191 return S_FALSE;
192 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
194 end:
195 if (playpos > *ret_writepos)
196 *pfree = playpos - *ret_writepos;
197 else if (playpos == *ret_writepos)
198 *pfree = This->buf_size - wfx->nBlockAlign;
199 else
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));
204 return S_FALSE;
206 return S_OK;
209 static HRESULT DSoundRender_HandleEndOfStream(DSoundRenderImpl *This)
211 while (This->renderer.filter.state == State_Running)
213 DWORD pos1, pos2;
214 DSoundRender_UpdatePositions(This, &pos1, &pos2);
215 if (pos1 == pos2)
216 break;
218 This->in_loop = 1;
219 LeaveCriticalSection(&This->renderer.csRenderLock);
220 WaitForSingleObject(This->renderer.flush_event, 10);
221 EnterCriticalSection(&This->renderer.csRenderLock);
222 This->in_loop = 0;
225 return S_OK;
228 static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* This, REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size)
230 HRESULT hr;
232 while (size && This->renderer.filter.state != State_Stopped) {
233 DWORD writepos, skip = 0, free, size1, size2, ret;
234 BYTE *buf1, *buf2;
236 if (This->renderer.filter.state == State_Running)
237 hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
238 else
239 hr = S_FALSE;
241 if (hr != S_OK) {
242 This->in_loop = 1;
243 LeaveCriticalSection(&This->renderer.csRenderLock);
244 ret = WaitForSingleObject(This->renderer.flush_event, 10);
245 EnterCriticalSection(&This->renderer.csRenderLock);
246 This->in_loop = 0;
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)
250 ERR("%x\n", ret);
251 continue;
253 tStart = -1;
255 if (skip)
256 FIXME("Sample dropped %u of %u bytes\n", skip, size);
257 if (skip >= size)
258 return S_OK;
259 data += skip;
260 size -= skip;
262 hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
263 if (hr != DS_OK) {
264 ERR("Unable to lock sound buffer! (%x)\n", hr);
265 break;
267 memcpy(buf1, data, size1);
268 if (size2)
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;
276 return S_OK;
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 */
283 return S_OK;
287 static HRESULT WINAPI DSoundRender_PrepareReceive(struct strmbase_renderer *iface, IMediaSample *pSample)
289 DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
290 HRESULT hr;
291 AM_MEDIA_TYPE *amt;
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);
309 if (FAILED(hr))
310 return VFW_E_TYPE_NOT_ACCEPTED;
311 FreeMediaType(orig);
312 CopyMediaType(orig, amt);
313 IMediaSample_SetMediaType(pSample, NULL);
316 else
317 return VFW_E_TYPE_NOT_ACCEPTED;
319 return S_OK;
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;
328 HRESULT hr;
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);
337 if (FAILED(hr))
339 ERR("Cannot get pointer to sample data (%x)\n", hr);
340 return hr;
343 hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
344 if (FAILED(hr)) {
345 ERR("Cannot get sample time (%x)\n", hr);
346 tStart = tStop = -1;
349 IMediaSample_IsDiscontinuity(pSample);
351 if (IMediaSample_IsPreroll(pSample) == S_OK)
353 TRACE("Preroll!\n");
354 return 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;
363 Quality q;
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;
368 else if (jitter < 0)
369 jitter = 0;
370 q.Type = (jitter > 0 ? Famine : Flood);
371 q.Proportion = 1000;
372 q.Late = jitter;
373 q.TimeStamp = tStart;
374 IQualityControl_Notify((IQualityControl *)This->renderer.qcimpl, &This->renderer.filter.IBaseFilter_iface, q);
376 return hr;
379 static HRESULT WINAPI DSoundRender_CheckMediaType(struct strmbase_renderer *iface, const AM_MEDIA_TYPE * pmt)
381 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
382 return S_FALSE;
384 if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
385 return S_FALSE;
387 return S_OK;
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;
416 HRESULT hr = S_OK;
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;
430 if (FAILED(hr))
431 ERR("Can't create sound buffer (%x)\n", hr);
433 if (SUCCEEDED(hr))
435 hr = IDirectSoundBuffer_SetVolume(This->dsbuffer, This->volume);
436 if (FAILED(hr))
437 ERR("Can't set volume to %d (%x)\n", This->volume, hr);
439 hr = IDirectSoundBuffer_SetPan(This->dsbuffer, This->pan);
440 if (FAILED(hr))
441 ERR("Can't set pan to %d (%x)\n", This->pan, hr);
442 hr = S_OK;
445 if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED)
447 if (This->dsbuffer)
448 IDirectSoundBuffer_Release(This->dsbuffer);
449 This->dsbuffer = NULL;
452 return hr;
455 static HRESULT WINAPI DSoundRender_BreakConnect(struct strmbase_renderer *iface)
457 DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
459 TRACE("(%p)->()\n", iface);
461 if (This->dsbuffer)
462 IDirectSoundBuffer_Release(This->dsbuffer);
463 This->dsbuffer = NULL;
465 return S_OK;
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);
478 if (This->dsbuffer)
480 LPBYTE buffer;
481 DWORD size;
483 /* Force a reset */
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;
490 return S_OK;
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;
500 if (filter->dsound)
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;
518 else
519 return E_NOINTERFACE;
521 IUnknown_AddRef((IUnknown *)*out);
522 return S_OK;
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,
543 REFIID riid,
544 LPVOID*ppvObj) {
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);
571 *count = 1;
572 return S_OK;
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)
585 ITypeInfo *typeinfo;
586 HRESULT hr;
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);
596 return hr;
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)
602 ITypeInfo *typeinfo;
603 HRESULT hr;
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);
613 return hr;
616 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
617 LONG lVolume) {
618 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
620 TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
622 if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
623 return E_INVALIDARG;
625 if (This->dsbuffer) {
626 if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
627 return E_FAIL;
630 This->volume = lVolume;
631 return S_OK;
634 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
635 LONG *plVolume) {
636 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
638 TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
640 if (!plVolume)
641 return E_POINTER;
643 *plVolume = This->volume;
644 return S_OK;
647 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
648 LONG lBalance) {
649 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
651 TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
653 if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
654 return E_INVALIDARG;
656 if (This->dsbuffer) {
657 if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
658 return E_FAIL;
661 This->pan = lBalance;
662 return S_OK;
665 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
666 LONG *plBalance) {
667 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
669 TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
671 if (!plBalance)
672 return E_POINTER;
674 *plBalance = This->pan;
675 return S_OK;
678 static const IBasicAudioVtbl IBasicAudio_Vtbl =
680 Basicaudio_QueryInterface,
681 Basicaudio_AddRef,
682 Basicaudio_Release,
683 basic_audio_GetTypeInfoCount,
684 basic_audio_GetTypeInfo,
685 basic_audio_GetIDsOfNames,
686 basic_audio_Invoke,
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,
695 REFIID riid,
696 LPVOID*ppvObj)
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);
730 return E_NOTIMPL;
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);
739 return E_NOTIMPL;
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);
748 return E_NOTIMPL;
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);
757 return E_NOTIMPL;
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);
766 return E_NOTIMPL;
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);
775 return E_NOTIMPL;
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);
784 return E_NOTIMPL;
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);
793 return E_NOTIMPL;
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;
820 HRESULT hr;
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);
830 return hr;
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);
838 return hr;
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);
849 return hr;
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);
859 return hr;
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;
872 return S_OK;