dmime: Include dmobject.h in dmime_private.h.
[wine.git] / dlls / quartz / dsoundrender.c
blob38a7d6f7ad7db80e0f2726f17be7995b1f6520a1
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 struct dsound_render
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(). */
55 HANDLE state_event;
56 /* Signaled when a flush or state change occurs, i.e. anything that needs
57 * to immediately unblock the streaming thread. */
58 HANDLE flush_event;
59 REFERENCE_TIME stream_start;
60 BOOL eos;
62 IDirectSound8 *dsound;
63 LPDIRECTSOUNDBUFFER dsbuffer;
64 DWORD buf_size;
65 DWORD last_playpos, writepos;
67 LONG volume;
68 LONG pan;
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;
96 return ret;
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;
104 ret /= 10000000;
105 ret -= ret % wfx->nBlockAlign;
106 return ret;
109 static void DSoundRender_UpdatePositions(struct dsound_render *This, DWORD *seqwritepos, DWORD *minwritepos)
111 WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
112 BYTE *buf1, *buf2;
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;
125 else
126 adv = playpos - old_playpos;
127 This->last_playpos = playpos;
128 if (adv) {
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) {
137 if (writepos_set) {
138 This->writepos = This->buf_size;
139 FIXME("Underrun of data occurred!\n");
141 *seqwritepos = writepos;
142 } else
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;
159 } else
160 write_at = -1;
162 if (writepos == min_writepos)
163 max_lag = 0;
165 *skip = 0;
166 if (write_at < 0) {
167 *ret_writepos = writepos;
168 goto end;
171 if (writepos >= playpos)
172 writepos_t = cur + time_from_pos(This, writepos - playpos);
173 else
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);
188 else
189 min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos);
190 past = min_writepos_t - write_at;
191 if (past >= 0) {
192 DWORD skipbytes = pos_from_time(This, past);
193 WARN("Skipping %lu bytes.\n", skipbytes);
194 *skip = skipbytes;
195 *ret_writepos = min_writepos;
196 } else {
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 */ {
202 DWORD aheadbytes;
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)
207 return S_FALSE;
208 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
210 end:
211 if (playpos >= *ret_writepos)
212 *pfree = playpos - *ret_writepos;
213 else
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));
218 return S_FALSE;
220 return S_OK;
223 static HRESULT DSoundRender_HandleEndOfStream(struct dsound_render *This)
225 while (This->filter.state == State_Running)
227 DWORD pos1, pos2;
228 DSoundRender_UpdatePositions(This, &pos1, &pos2);
229 if (pos1 == pos2)
230 break;
232 WaitForSingleObject(This->flush_event, 10);
235 return S_OK;
238 static HRESULT DSoundRender_SendSampleData(struct dsound_render *This,
239 REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size)
241 HRESULT hr;
243 while (size && This->filter.state != State_Stopped) {
244 DWORD writepos, skip = 0, free, size1, size2, ret;
245 BYTE *buf1, *buf2;
247 if (This->filter.state == State_Running)
248 hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
249 else
250 hr = S_FALSE;
252 if (hr != S_OK) {
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);
258 continue;
260 tStart = -1;
262 if (skip)
263 FIXME("Sample dropped %lu of %lu bytes.\n", skip, size);
264 if (skip >= size)
265 return S_OK;
266 data += skip;
267 size -= skip;
269 hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
270 if (hr != DS_OK) {
271 ERR("Failed to lock sound buffer, hr %#lx.\n", hr);
272 break;
274 memcpy(buf1, data, size1);
275 if (size2)
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;
283 return S_OK;
286 static HRESULT DSoundRender_PrepareReceive(struct dsound_render *This, IMediaSample *pSample)
288 HRESULT hr;
289 AM_MEDIA_TYPE *amt;
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);
310 if (FAILED(hr))
311 return VFW_E_TYPE_NOT_ACCEPTED;
312 FreeMediaType(orig);
313 CopyMediaType(orig, amt);
314 IMediaSample_SetMediaType(pSample, NULL);
317 else
318 return VFW_E_TYPE_NOT_ACCEPTED;
320 return S_OK;
323 static HRESULT DSoundRender_DoRenderSample(struct dsound_render *This, IMediaSample *pSample)
325 LPBYTE pbSrcStream = NULL;
326 LONG cbSrcStream = 0;
327 REFERENCE_TIME tStart, tStop;
328 HRESULT hr;
330 hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
331 if (FAILED(hr))
333 ERR("Failed to get buffer pointer, hr %#lx.\n", hr);
334 return hr;
337 hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
338 if (FAILED(hr)) {
339 ERR("Failed to get sample time, hr %#lx.\n", hr);
340 tStart = tStop = -1;
343 if (IMediaSample_IsPreroll(pSample) == S_OK)
345 TRACE("Preroll!\n");
346 return 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;
357 HRESULT hr;
359 if (filter->eos || filter->sink.flushing)
360 return S_FALSE;
362 if (filter->filter.state == State_Stopped)
363 return VFW_E_WRONG_STATE;
365 if (FAILED(hr = DSoundRender_PrepareReceive(filter, sample)))
366 return hr;
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;
383 else
384 return E_NOINTERFACE;
386 IUnknown_AddRef((IUnknown *)*out);
387 return S_OK;
390 static HRESULT dsound_render_sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE * pmt)
392 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
393 return S_FALSE;
395 return S_OK;
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;
402 HRESULT hr = S_OK;
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;
416 if (FAILED(hr))
417 ERR("Failed to create sound buffer, hr %#lx.\n", hr);
419 if (SUCCEEDED(hr))
421 hr = IDirectSoundBuffer_SetVolume(This->dsbuffer, This->volume);
422 if (FAILED(hr))
423 ERR("Failed to set volume to %ld, hr %#lx.\n", This->volume, hr);
425 hr = IDirectSoundBuffer_SetPan(This->dsbuffer, This->pan);
426 if (FAILED(hr))
427 ERR("Failed to set pan to %ld, hr %#lx.\n", This->pan, hr);
428 hr = S_OK;
431 if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED)
433 if (This->dsbuffer)
434 IDirectSoundBuffer_Release(This->dsbuffer);
435 This->dsbuffer = NULL;
438 return hr;
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);
447 if (This->dsbuffer)
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;
457 void *buffer;
458 DWORD size;
460 filter->eos = TRUE;
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);
479 return S_OK;
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);
487 return S_OK;
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);
496 filter->eos = FALSE;
497 strmbase_passthrough_invalidate_time(&filter->passthrough);
498 ResetEvent(filter->flush_event);
500 if (filter->dsbuffer)
502 void *buffer;
503 DWORD size;
505 /* Force a reset */
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);
513 return S_OK;
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;
535 if (filter->dsound)
536 IDirectSound8_Release(filter->dsound);
537 filter->dsound = NULL;
539 IUnknown_Release(filter->system_clock);
541 if (filter->sink.pin.peer)
542 IPin_Disconnect(filter->sink.pin.peer);
543 IPin_Disconnect(&filter->sink.pin.IPin_iface);
544 strmbase_sink_cleanup(&filter->sink);
546 CloseHandle(filter->state_event);
547 CloseHandle(filter->flush_event);
549 strmbase_passthrough_cleanup(&filter->passthrough);
550 strmbase_filter_cleanup(&filter->filter);
551 free(filter);
554 static struct strmbase_pin *dsound_render_get_pin(struct strmbase_filter *iface, unsigned int index)
556 struct dsound_render *filter = impl_from_strmbase_filter(iface);
558 if (index == 0)
559 return &filter->sink.pin;
560 return NULL;
563 static HRESULT dsound_render_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
565 struct dsound_render *filter = impl_from_strmbase_filter(iface);
567 if (IsEqualGUID(iid, &IID_IAMDirectSound))
568 *out = &filter->IAMDirectSound_iface;
569 else if (IsEqualGUID(iid, &IID_IBasicAudio))
570 *out = &filter->IBasicAudio_iface;
571 else if (IsEqualGUID(iid, &IID_IMediaPosition))
572 *out = &filter->passthrough.IMediaPosition_iface;
573 else if (IsEqualGUID(iid, &IID_IMediaSeeking))
574 *out = &filter->passthrough.IMediaSeeking_iface;
575 else if (IsEqualGUID(iid, &IID_IQualityControl))
576 *out = &filter->IQualityControl_iface;
577 else if (IsEqualGUID(iid, &IID_IReferenceClock))
578 return IUnknown_QueryInterface(filter->system_clock, iid, out);
579 else
580 return E_NOINTERFACE;
582 IUnknown_AddRef((IUnknown *)*out);
583 return S_OK;
586 static HRESULT dsound_render_init_stream(struct strmbase_filter *iface)
588 struct dsound_render *filter = impl_from_strmbase_filter(iface);
590 if (filter->sink.pin.peer)
591 ResetEvent(filter->state_event);
592 filter->eos = FALSE;
593 ResetEvent(filter->flush_event);
595 return filter->sink.pin.peer ? S_FALSE : S_OK;
598 static HRESULT dsound_render_start_stream(struct strmbase_filter *iface, REFERENCE_TIME start)
600 struct dsound_render *filter = impl_from_strmbase_filter(iface);
601 IFilterGraph *graph = filter->filter.graph;
602 IMediaEventSink *event_sink;
604 filter->stream_start = start;
606 SetEvent(filter->state_event);
608 if (filter->sink.pin.peer)
609 IDirectSoundBuffer_Play(filter->dsbuffer, 0, 0, DSBPLAY_LOOPING);
611 if ((filter->eos || !filter->sink.pin.peer) && graph
612 && SUCCEEDED(IFilterGraph_QueryInterface(graph,
613 &IID_IMediaEventSink, (void **)&event_sink)))
615 IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK,
616 (LONG_PTR)&filter->filter.IBaseFilter_iface);
617 IMediaEventSink_Release(event_sink);
620 return S_OK;
623 static HRESULT dsound_render_stop_stream(struct strmbase_filter *iface)
625 struct dsound_render *filter = impl_from_strmbase_filter(iface);
627 if (filter->sink.pin.peer)
629 IDirectSoundBuffer_Stop(filter->dsbuffer);
630 filter->writepos = filter->buf_size;
632 return S_OK;
635 static HRESULT dsound_render_cleanup_stream(struct strmbase_filter *iface)
637 struct dsound_render *filter = impl_from_strmbase_filter(iface);
639 strmbase_passthrough_invalidate_time(&filter->passthrough);
640 SetEvent(filter->state_event);
641 SetEvent(filter->flush_event);
643 return S_OK;
646 static HRESULT dsound_render_wait_state(struct strmbase_filter *iface, DWORD timeout)
648 struct dsound_render *filter = impl_from_strmbase_filter(iface);
650 if (WaitForSingleObject(filter->state_event, timeout) == WAIT_TIMEOUT)
651 return VFW_S_STATE_INTERMEDIATE;
652 return S_OK;
655 static const struct strmbase_filter_ops filter_ops =
657 .filter_destroy = dsound_render_destroy,
658 .filter_get_pin = dsound_render_get_pin,
659 .filter_query_interface = dsound_render_query_interface,
660 .filter_init_stream = dsound_render_init_stream,
661 .filter_start_stream = dsound_render_start_stream,
662 .filter_stop_stream = dsound_render_stop_stream,
663 .filter_cleanup_stream = dsound_render_cleanup_stream,
664 .filter_wait_state = dsound_render_wait_state,
667 /*** IUnknown methods ***/
668 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
669 REFIID riid,
670 LPVOID*ppvObj) {
671 struct dsound_render *This = impl_from_IBasicAudio(iface);
673 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
675 return IUnknown_QueryInterface(This->filter.outer_unk, riid, ppvObj);
678 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
679 struct dsound_render *This = impl_from_IBasicAudio(iface);
681 TRACE("(%p/%p)->()\n", This, iface);
683 return IUnknown_AddRef(This->filter.outer_unk);
686 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
687 struct dsound_render *This = impl_from_IBasicAudio(iface);
689 TRACE("(%p/%p)->()\n", This, iface);
691 return IUnknown_Release(This->filter.outer_unk);
694 HRESULT WINAPI basic_audio_GetTypeInfoCount(IBasicAudio *iface, UINT *count)
696 TRACE("iface %p, count %p.\n", iface, count);
697 *count = 1;
698 return S_OK;
701 HRESULT WINAPI basic_audio_GetTypeInfo(IBasicAudio *iface, UINT index,
702 LCID lcid, ITypeInfo **typeinfo)
704 TRACE("iface %p, index %u, lcid %#lx, typeinfo %p.\n", iface, index, lcid, typeinfo);
705 return strmbase_get_typeinfo(IBasicAudio_tid, typeinfo);
708 HRESULT WINAPI basic_audio_GetIDsOfNames(IBasicAudio *iface, REFIID iid,
709 LPOLESTR *names, UINT count, LCID lcid, DISPID *ids)
711 ITypeInfo *typeinfo;
712 HRESULT hr;
714 TRACE("iface %p, iid %s, names %p, count %u, lcid %#lx, ids %p.\n",
715 iface, debugstr_guid(iid), names, count, lcid, ids);
717 if (SUCCEEDED(hr = strmbase_get_typeinfo(IBasicAudio_tid, &typeinfo)))
719 hr = ITypeInfo_GetIDsOfNames(typeinfo, names, count, ids);
720 ITypeInfo_Release(typeinfo);
722 return hr;
725 static HRESULT WINAPI basic_audio_Invoke(IBasicAudio *iface, DISPID id, REFIID iid, LCID lcid,
726 WORD flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *error_arg)
728 ITypeInfo *typeinfo;
729 HRESULT hr;
731 TRACE("iface %p, id %ld, iid %s, lcid %#lx, flags %#x, params %p, result %p, excepinfo %p, error_arg %p.\n",
732 iface, id, debugstr_guid(iid), lcid, flags, params, result, excepinfo, error_arg);
734 if (SUCCEEDED(hr = strmbase_get_typeinfo(IBasicAudio_tid, &typeinfo)))
736 hr = ITypeInfo_Invoke(typeinfo, iface, id, flags, params, result, excepinfo, error_arg);
737 ITypeInfo_Release(typeinfo);
739 return hr;
742 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
743 LONG lVolume) {
744 struct dsound_render *This = impl_from_IBasicAudio(iface);
746 TRACE("filter %p, volume %ld.\n", This, lVolume);
748 if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
749 return E_INVALIDARG;
751 if (This->dsbuffer) {
752 if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
753 return E_FAIL;
756 This->volume = lVolume;
757 return S_OK;
760 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
761 LONG *plVolume) {
762 struct dsound_render *This = impl_from_IBasicAudio(iface);
764 TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
766 if (!plVolume)
767 return E_POINTER;
769 *plVolume = This->volume;
770 return S_OK;
773 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
774 LONG lBalance) {
775 struct dsound_render *This = impl_from_IBasicAudio(iface);
777 TRACE("filter %p, balance %ld.\n", This, lBalance);
779 if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
780 return E_INVALIDARG;
782 if (This->dsbuffer) {
783 if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
784 return E_FAIL;
787 This->pan = lBalance;
788 return S_OK;
791 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
792 LONG *plBalance) {
793 struct dsound_render *This = impl_from_IBasicAudio(iface);
795 TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
797 if (!plBalance)
798 return E_POINTER;
800 *plBalance = This->pan;
801 return S_OK;
804 static const IBasicAudioVtbl IBasicAudio_Vtbl =
806 Basicaudio_QueryInterface,
807 Basicaudio_AddRef,
808 Basicaudio_Release,
809 basic_audio_GetTypeInfoCount,
810 basic_audio_GetTypeInfo,
811 basic_audio_GetIDsOfNames,
812 basic_audio_Invoke,
813 Basicaudio_put_Volume,
814 Basicaudio_get_Volume,
815 Basicaudio_put_Balance,
816 Basicaudio_get_Balance
819 /*** IUnknown methods ***/
820 static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
821 REFIID riid,
822 LPVOID*ppvObj)
824 struct dsound_render *This = impl_from_IAMDirectSound(iface);
826 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
828 return IUnknown_QueryInterface(This->filter.outer_unk, riid, ppvObj);
831 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
833 struct dsound_render *This = impl_from_IAMDirectSound(iface);
835 TRACE("(%p/%p)->()\n", This, iface);
837 return IUnknown_AddRef(This->filter.outer_unk);
840 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
842 struct dsound_render *This = impl_from_IAMDirectSound(iface);
844 TRACE("(%p/%p)->()\n", This, iface);
846 return IUnknown_Release(This->filter.outer_unk);
849 /*** IAMDirectSound methods ***/
850 static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface, IDirectSound **ds)
852 struct dsound_render *This = impl_from_IAMDirectSound(iface);
854 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
856 return E_NOTIMPL;
859 static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
861 struct dsound_render *This = impl_from_IAMDirectSound(iface);
863 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
865 return E_NOTIMPL;
868 static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
870 struct dsound_render *This = impl_from_IAMDirectSound(iface);
872 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
874 return E_NOTIMPL;
877 static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
879 struct dsound_render *This = impl_from_IAMDirectSound(iface);
881 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
883 return E_NOTIMPL;
886 static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
888 struct dsound_render *This = impl_from_IAMDirectSound(iface);
890 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
892 return E_NOTIMPL;
895 static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
897 struct dsound_render *This = impl_from_IAMDirectSound(iface);
899 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
901 return E_NOTIMPL;
904 static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgaudible)
906 struct dsound_render *This = impl_from_IAMDirectSound(iface);
908 FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgaudible);
910 return E_NOTIMPL;
913 static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND *hwnd, BOOL *bgaudible)
915 struct dsound_render *This = impl_from_IAMDirectSound(iface);
917 FIXME("(%p/%p)->(%p,%p): stub\n", This, iface, hwnd, bgaudible);
919 return E_NOTIMPL;
922 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
924 AMDirectSound_QueryInterface,
925 AMDirectSound_AddRef,
926 AMDirectSound_Release,
927 AMDirectSound_GetDirectSoundInterface,
928 AMDirectSound_GetPrimaryBufferInterface,
929 AMDirectSound_GetSecondaryBufferInterface,
930 AMDirectSound_ReleaseDirectSoundInterface,
931 AMDirectSound_ReleasePrimaryBufferInterface,
932 AMDirectSound_ReleaseSecondaryBufferInterface,
933 AMDirectSound_SetFocusWindow,
934 AMDirectSound_GetFocusWindow
937 static struct dsound_render *impl_from_IQualityControl(IQualityControl *iface)
939 return CONTAINING_RECORD(iface, struct dsound_render, IQualityControl_iface);
942 static HRESULT WINAPI dsound_render_qc_QueryInterface(IQualityControl *iface,
943 REFIID iid, void **out)
945 struct dsound_render *filter = impl_from_IQualityControl(iface);
946 return IUnknown_QueryInterface(filter->filter.outer_unk, iid, out);
949 static ULONG WINAPI dsound_render_qc_AddRef(IQualityControl *iface)
951 struct dsound_render *filter = impl_from_IQualityControl(iface);
952 return IUnknown_AddRef(filter->filter.outer_unk);
955 static ULONG WINAPI dsound_render_qc_Release(IQualityControl *iface)
957 struct dsound_render *filter = impl_from_IQualityControl(iface);
958 return IUnknown_Release(filter->filter.outer_unk);
961 static HRESULT WINAPI dsound_render_qc_Notify(IQualityControl *iface,
962 IBaseFilter *sender, Quality q)
964 struct dsound_render *filter = impl_from_IQualityControl(iface);
966 FIXME("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s, stub!\n",
967 filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp));
969 return E_NOTIMPL;
972 static HRESULT WINAPI dsound_render_qc_SetSink(IQualityControl *iface, IQualityControl *sink)
974 struct dsound_render *filter = impl_from_IQualityControl(iface);
976 FIXME("filter %p, sink %p, stub!\n", filter, sink);
978 return E_NOTIMPL;
981 static const IQualityControlVtbl dsound_render_qc_vtbl =
983 dsound_render_qc_QueryInterface,
984 dsound_render_qc_AddRef,
985 dsound_render_qc_Release,
986 dsound_render_qc_Notify,
987 dsound_render_qc_SetSink,
990 HRESULT dsound_render_create(IUnknown *outer, IUnknown **out)
992 static const DSBUFFERDESC buffer_desc = {
993 .dwSize = sizeof(DSBUFFERDESC),
994 .dwFlags = DSBCAPS_PRIMARYBUFFER,
997 struct dsound_render *object;
998 IDirectSoundBuffer *buffer;
999 HRESULT hr;
1001 if (!(object = calloc(1, sizeof(*object))))
1002 return E_OUTOFMEMORY;
1004 strmbase_filter_init(&object->filter, outer, &CLSID_DSoundRender, &filter_ops);
1006 if (FAILED(hr = system_clock_create(&object->filter.IUnknown_inner, &object->system_clock)))
1008 strmbase_filter_cleanup(&object->filter);
1009 free(object);
1010 return hr;
1013 if (FAILED(hr = DirectSoundCreate8(NULL, &object->dsound, NULL)))
1015 IUnknown_Release(object->system_clock);
1016 strmbase_filter_cleanup(&object->filter);
1017 free(object);
1018 return hr;
1021 if (FAILED(hr = IDirectSound8_SetCooperativeLevel(object->dsound,
1022 GetDesktopWindow(), DSSCL_PRIORITY)))
1024 IDirectSound8_Release(object->dsound);
1025 IUnknown_Release(object->system_clock);
1026 strmbase_filter_cleanup(&object->filter);
1027 free(object);
1028 return hr;
1031 if (SUCCEEDED(hr = IDirectSound8_CreateSoundBuffer(object->dsound,
1032 &buffer_desc, &buffer, NULL)))
1034 IDirectSoundBuffer_Play(buffer, 0, 0, DSBPLAY_LOOPING);
1035 IDirectSoundBuffer_Release(buffer);
1038 strmbase_passthrough_init(&object->passthrough, (IUnknown *)&object->filter.IBaseFilter_iface);
1039 ISeekingPassThru_Init(&object->passthrough.ISeekingPassThru_iface, TRUE, &object->sink.pin.IPin_iface);
1041 strmbase_sink_init(&object->sink, &object->filter, L"Audio Input pin (rendered)", &sink_ops, NULL);
1043 object->state_event = CreateEventW(NULL, TRUE, TRUE, NULL);
1044 object->flush_event = CreateEventW(NULL, TRUE, TRUE, NULL);
1046 object->IBasicAudio_iface.lpVtbl = &IBasicAudio_Vtbl;
1047 object->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl;
1048 object->IQualityControl_iface.lpVtbl = &dsound_render_qc_vtbl;
1050 TRACE("Created DirectSound renderer %p.\n", object);
1051 *out = &object->filter.IUnknown_inner;
1053 return S_OK;