widl: Generate helper macros for WinRT implementation.
[wine/zf.git] / dlls / quartz / dsoundrender.c
blob6c6732e468a7f9ffb1e1101bb547343df529caa1
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 REFERENCE_TIME play_time;
69 LONG volume;
70 LONG pan;
73 static struct dsound_render *impl_from_strmbase_pin(struct strmbase_pin *iface)
75 return CONTAINING_RECORD(iface, struct dsound_render, sink.pin);
78 static struct dsound_render *impl_from_strmbase_filter(struct strmbase_filter *iface)
80 return CONTAINING_RECORD(iface, struct dsound_render, filter);
83 static struct dsound_render *impl_from_IBasicAudio(IBasicAudio *iface)
85 return CONTAINING_RECORD(iface, struct dsound_render, IBasicAudio_iface);
88 static struct dsound_render *impl_from_IAMDirectSound(IAMDirectSound *iface)
90 return CONTAINING_RECORD(iface, struct dsound_render, IAMDirectSound_iface);
93 static REFERENCE_TIME time_from_pos(struct dsound_render *This, DWORD pos)
95 WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
96 REFERENCE_TIME ret = 10000000;
97 ret = ret * pos / wfx->nAvgBytesPerSec;
98 return ret;
101 static DWORD pos_from_time(struct dsound_render *This, REFERENCE_TIME time)
103 WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
104 REFERENCE_TIME ret = time;
105 ret *= wfx->nAvgBytesPerSec;
106 ret /= 10000000;
107 ret -= ret % wfx->nBlockAlign;
108 return ret;
111 static void DSoundRender_UpdatePositions(struct dsound_render *This, DWORD *seqwritepos, DWORD *minwritepos)
113 WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
114 BYTE *buf1, *buf2;
115 DWORD size1, size2, playpos, writepos, old_writepos, old_playpos, adv;
116 BOOL writepos_set = This->writepos < This->buf_size;
118 /* Update position and zero */
119 old_writepos = This->writepos;
120 old_playpos = This->last_playpos;
121 if (old_writepos <= old_playpos)
122 old_writepos += This->buf_size;
124 IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, &playpos, &writepos);
125 if (old_playpos > playpos) {
126 adv = This->buf_size + playpos - old_playpos;
127 This->play_time += time_from_pos(This, This->buf_size);
128 } else
129 adv = playpos - old_playpos;
130 This->last_playpos = playpos;
131 if (adv) {
132 TRACE("Moving from %u to %u: clearing %u bytes\n", old_playpos, playpos, adv);
133 IDirectSoundBuffer_Lock(This->dsbuffer, old_playpos, adv, (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
134 memset(buf1, wfx->wBitsPerSample == 8 ? 128 : 0, size1);
135 memset(buf2, wfx->wBitsPerSample == 8 ? 128 : 0, size2);
136 IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
138 *minwritepos = writepos;
139 if (!writepos_set || old_writepos < writepos) {
140 if (writepos_set) {
141 This->writepos = This->buf_size;
142 FIXME("Underrun of data occurred!\n");
144 *seqwritepos = writepos;
145 } else
146 *seqwritepos = This->writepos;
149 static HRESULT DSoundRender_GetWritePos(struct dsound_render *This,
150 DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip)
152 WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
153 DWORD writepos, min_writepos, playpos;
154 REFERENCE_TIME max_lag = 50 * 10000;
155 REFERENCE_TIME cur, writepos_t, delta_t;
157 DSoundRender_UpdatePositions(This, &writepos, &min_writepos);
158 playpos = This->last_playpos;
159 if (This->filter.clock)
161 IReferenceClock_GetTime(This->filter.clock, &cur);
162 cur -= This->stream_start;
163 } else
164 write_at = -1;
166 if (writepos == min_writepos)
167 max_lag = 0;
169 *skip = 0;
170 if (write_at < 0) {
171 *ret_writepos = writepos;
172 goto end;
175 if (writepos >= playpos)
176 writepos_t = cur + time_from_pos(This, writepos - playpos);
177 else
178 writepos_t = cur + time_from_pos(This, This->buf_size + writepos - playpos);
180 /* write_at: Starting time of sample */
181 /* cur: current time of play position */
182 /* writepos_t: current time of our pointer play position */
183 delta_t = write_at - writepos_t;
184 if (delta_t >= -max_lag && delta_t <= max_lag) {
185 TRACE("Continuing from old position\n");
186 *ret_writepos = writepos;
187 } else if (delta_t < 0) {
188 REFERENCE_TIME past, min_writepos_t;
189 WARN("Delta too big %s/%s, overwriting old data or even skipping\n", debugstr_time(delta_t), debugstr_time(max_lag));
190 if (min_writepos >= playpos)
191 min_writepos_t = cur + time_from_pos(This, min_writepos - playpos);
192 else
193 min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos);
194 past = min_writepos_t - write_at;
195 if (past >= 0) {
196 DWORD skipbytes = pos_from_time(This, past);
197 WARN("Skipping %u bytes\n", skipbytes);
198 *skip = skipbytes;
199 *ret_writepos = min_writepos;
200 } else {
201 DWORD aheadbytes = pos_from_time(This, -past);
202 WARN("Advancing %u bytes\n", aheadbytes);
203 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
205 } else /* delta_t > 0 */ {
206 DWORD aheadbytes;
207 WARN("Delta too big %s/%s, too far ahead\n", debugstr_time(delta_t), debugstr_time(max_lag));
208 aheadbytes = pos_from_time(This, delta_t);
209 WARN("Advancing %u bytes\n", aheadbytes);
210 if (delta_t >= DSoundRenderer_Max_Fill)
211 return S_FALSE;
212 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
214 end:
215 if (playpos > *ret_writepos)
216 *pfree = playpos - *ret_writepos;
217 else if (playpos == *ret_writepos)
218 *pfree = This->buf_size - wfx->nBlockAlign;
219 else
220 *pfree = This->buf_size + playpos - *ret_writepos;
221 if (time_from_pos(This, This->buf_size - *pfree) >= DSoundRenderer_Max_Fill) {
222 TRACE("Blocked: too full %s / %s\n", debugstr_time(time_from_pos(This, This->buf_size - *pfree)),
223 debugstr_time(DSoundRenderer_Max_Fill));
224 return S_FALSE;
226 return S_OK;
229 static HRESULT DSoundRender_HandleEndOfStream(struct dsound_render *This)
231 while (This->filter.state == State_Running)
233 DWORD pos1, pos2;
234 DSoundRender_UpdatePositions(This, &pos1, &pos2);
235 if (pos1 == pos2)
236 break;
238 WaitForSingleObject(This->flush_event, 10);
241 return S_OK;
244 static HRESULT DSoundRender_SendSampleData(struct dsound_render *This,
245 REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size)
247 HRESULT hr;
249 while (size && This->filter.state != State_Stopped) {
250 DWORD writepos, skip = 0, free, size1, size2, ret;
251 BYTE *buf1, *buf2;
253 if (This->filter.state == State_Running)
254 hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
255 else
256 hr = S_FALSE;
258 if (hr != S_OK) {
259 ret = WaitForSingleObject(This->flush_event, 10);
260 if (This->sink.flushing || This->filter.state == State_Stopped)
261 return This->filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE;
262 if (ret != WAIT_TIMEOUT)
263 ERR("%x\n", ret);
264 continue;
266 tStart = -1;
268 if (skip)
269 FIXME("Sample dropped %u of %u bytes\n", skip, size);
270 if (skip >= size)
271 return S_OK;
272 data += skip;
273 size -= skip;
275 hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
276 if (hr != DS_OK) {
277 ERR("Unable to lock sound buffer! (%x)\n", hr);
278 break;
280 memcpy(buf1, data, size1);
281 if (size2)
282 memcpy(buf2, data+size1, size2);
283 IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
284 This->writepos = (writepos + size1 + size2) % This->buf_size;
285 TRACE("Wrote %u bytes at %u, next at %u - (%u/%u)\n", size1+size2, writepos, This->writepos, free, size);
286 data += size1 + size2;
287 size -= size1 + size2;
289 return S_OK;
292 static HRESULT WINAPI DSoundRender_PrepareReceive(struct dsound_render *This, IMediaSample *pSample)
294 HRESULT hr;
295 AM_MEDIA_TYPE *amt;
297 if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
299 AM_MEDIA_TYPE *orig = &This->sink.pin.mt;
300 WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
301 WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
303 TRACE("Format change.\n");
304 strmbase_dump_media_type(amt);
306 if (origfmt->wFormatTag == newfmt->wFormatTag &&
307 origfmt->nChannels == newfmt->nChannels &&
308 origfmt->nBlockAlign == newfmt->nBlockAlign &&
309 origfmt->wBitsPerSample == newfmt->wBitsPerSample &&
310 origfmt->cbSize == newfmt->cbSize)
312 if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec)
314 hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer,
315 newfmt->nSamplesPerSec);
316 if (FAILED(hr))
317 return VFW_E_TYPE_NOT_ACCEPTED;
318 FreeMediaType(orig);
319 CopyMediaType(orig, amt);
320 IMediaSample_SetMediaType(pSample, NULL);
323 else
324 return VFW_E_TYPE_NOT_ACCEPTED;
326 return S_OK;
329 static HRESULT WINAPI DSoundRender_DoRenderSample(struct dsound_render *This, IMediaSample *pSample)
331 LPBYTE pbSrcStream = NULL;
332 LONG cbSrcStream = 0;
333 REFERENCE_TIME tStart, tStop;
334 HRESULT hr;
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 if (IMediaSample_IsPreroll(pSample) == S_OK)
351 TRACE("Preroll!\n");
352 return S_OK;
355 cbSrcStream = IMediaSample_GetActualDataLength(pSample);
356 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
358 return DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream);
361 static HRESULT WINAPI dsound_render_sink_Receive(struct strmbase_sink *iface, IMediaSample *sample)
363 struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
364 REFERENCE_TIME start, stop;
365 HRESULT hr;
367 if (filter->eos || filter->sink.flushing)
368 return S_FALSE;
370 if (filter->filter.state == State_Stopped)
371 return VFW_E_WRONG_STATE;
373 if (FAILED(hr = DSoundRender_PrepareReceive(filter, sample)))
374 return hr;
376 if (filter->filter.clock && SUCCEEDED(IMediaSample_GetTime(sample, &start, &stop)))
377 strmbase_passthrough_update_time(&filter->passthrough, start);
379 if (filter->filter.state == State_Paused)
380 SetEvent(filter->state_event);
382 return DSoundRender_DoRenderSample(filter, sample);
385 static HRESULT dsound_render_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
387 struct dsound_render *filter = impl_from_strmbase_pin(iface);
389 if (IsEqualGUID(iid, &IID_IMemInputPin))
390 *out = &filter->sink.IMemInputPin_iface;
391 else
392 return E_NOINTERFACE;
394 IUnknown_AddRef((IUnknown *)*out);
395 return S_OK;
398 static HRESULT dsound_render_sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE * pmt)
400 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
401 return S_FALSE;
403 return S_OK;
406 static HRESULT dsound_render_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
408 struct dsound_render *This = impl_from_strmbase_pin(&iface->pin);
409 const WAVEFORMATEX *format = (WAVEFORMATEX *)mt->pbFormat;
410 HRESULT hr = S_OK;
411 DSBUFFERDESC buf_desc;
413 This->buf_size = format->nAvgBytesPerSec;
415 memset(&buf_desc,0,sizeof(DSBUFFERDESC));
416 buf_desc.dwSize = sizeof(DSBUFFERDESC);
417 buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
418 DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS |
419 DSBCAPS_GETCURRENTPOSITION2;
420 buf_desc.dwBufferBytes = This->buf_size;
421 buf_desc.lpwfxFormat = (WAVEFORMATEX *)format;
422 hr = IDirectSound8_CreateSoundBuffer(This->dsound, &buf_desc, &This->dsbuffer, NULL);
423 This->writepos = This->buf_size;
424 if (FAILED(hr))
425 ERR("Can't create sound buffer (%x)\n", hr);
427 if (SUCCEEDED(hr))
429 hr = IDirectSoundBuffer_SetVolume(This->dsbuffer, This->volume);
430 if (FAILED(hr))
431 ERR("Can't set volume to %d (%x)\n", This->volume, hr);
433 hr = IDirectSoundBuffer_SetPan(This->dsbuffer, This->pan);
434 if (FAILED(hr))
435 ERR("Can't set pan to %d (%x)\n", This->pan, hr);
436 hr = S_OK;
439 if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED)
441 if (This->dsbuffer)
442 IDirectSoundBuffer_Release(This->dsbuffer);
443 This->dsbuffer = NULL;
446 return hr;
449 static void dsound_render_sink_disconnect(struct strmbase_sink *iface)
451 struct dsound_render *This = impl_from_strmbase_pin(&iface->pin);
453 TRACE("(%p)->()\n", iface);
455 if (This->dsbuffer)
456 IDirectSoundBuffer_Release(This->dsbuffer);
457 This->dsbuffer = NULL;
460 static HRESULT dsound_render_sink_eos(struct strmbase_sink *iface)
462 struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
463 IFilterGraph *graph = filter->filter.graph;
464 IMediaEventSink *event_sink;
465 void *buffer;
466 DWORD size;
468 filter->eos = TRUE;
470 if (graph && SUCCEEDED(IFilterGraph_QueryInterface(graph,
471 &IID_IMediaEventSink, (void **)&event_sink)))
473 IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK,
474 (LONG_PTR)&filter->filter.IBaseFilter_iface);
475 IMediaEventSink_Release(event_sink);
477 strmbase_passthrough_eos(&filter->passthrough);
478 SetEvent(filter->state_event);
480 DSoundRender_HandleEndOfStream(filter);
482 IDirectSoundBuffer_Lock(filter->dsbuffer, 0, 0, &buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
483 memset(buffer, 0, size);
484 IDirectSoundBuffer_Unlock(filter->dsbuffer, buffer, size, NULL, 0);
486 return S_OK;
489 static HRESULT dsound_render_sink_begin_flush(struct strmbase_sink *iface)
491 struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
493 SetEvent(filter->flush_event);
494 return S_OK;
497 static HRESULT dsound_render_sink_end_flush(struct strmbase_sink *iface)
499 struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
501 EnterCriticalSection(&filter->filter.stream_cs);
503 filter->eos = FALSE;
504 strmbase_passthrough_invalidate_time(&filter->passthrough);
505 ResetEvent(filter->flush_event);
507 if (filter->dsbuffer)
509 void *buffer;
510 DWORD size;
512 /* Force a reset */
513 IDirectSoundBuffer_Lock(filter->dsbuffer, 0, 0, &buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
514 memset(buffer, 0, size);
515 IDirectSoundBuffer_Unlock(filter->dsbuffer, buffer, size, NULL, 0);
516 filter->writepos = filter->buf_size;
519 LeaveCriticalSection(&filter->filter.stream_cs);
520 return S_OK;
523 static const struct strmbase_sink_ops sink_ops =
525 .base.pin_query_interface = dsound_render_sink_query_interface,
526 .base.pin_query_accept = dsound_render_sink_query_accept,
527 .pfnReceive = dsound_render_sink_Receive,
528 .sink_connect = dsound_render_sink_connect,
529 .sink_disconnect = dsound_render_sink_disconnect,
530 .sink_eos = dsound_render_sink_eos,
531 .sink_begin_flush = dsound_render_sink_begin_flush,
532 .sink_end_flush = dsound_render_sink_end_flush,
535 static void dsound_render_destroy(struct strmbase_filter *iface)
537 struct dsound_render *filter = impl_from_strmbase_filter(iface);
539 if (filter->dsbuffer)
540 IDirectSoundBuffer_Release(filter->dsbuffer);
541 filter->dsbuffer = NULL;
542 if (filter->dsound)
543 IDirectSound8_Release(filter->dsound);
544 filter->dsound = NULL;
546 if (filter->sink.pin.peer)
547 IPin_Disconnect(filter->sink.pin.peer);
548 IPin_Disconnect(&filter->sink.pin.IPin_iface);
549 strmbase_sink_cleanup(&filter->sink);
551 CloseHandle(filter->state_event);
552 CloseHandle(filter->flush_event);
554 strmbase_passthrough_cleanup(&filter->passthrough);
555 strmbase_filter_cleanup(&filter->filter);
556 free(filter);
558 InterlockedDecrement(&object_locks);
561 static struct strmbase_pin *dsound_render_get_pin(struct strmbase_filter *iface, unsigned int index)
563 struct dsound_render *filter = impl_from_strmbase_filter(iface);
565 if (index == 0)
566 return &filter->sink.pin;
567 return NULL;
570 static HRESULT dsound_render_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
572 struct dsound_render *filter = impl_from_strmbase_filter(iface);
574 if (IsEqualGUID(iid, &IID_IAMDirectSound))
575 *out = &filter->IAMDirectSound_iface;
576 else if (IsEqualGUID(iid, &IID_IBasicAudio))
577 *out = &filter->IBasicAudio_iface;
578 else if (IsEqualGUID(iid, &IID_IMediaPosition))
579 *out = &filter->passthrough.IMediaPosition_iface;
580 else if (IsEqualGUID(iid, &IID_IMediaSeeking))
581 *out = &filter->passthrough.IMediaSeeking_iface;
582 else if (IsEqualGUID(iid, &IID_IQualityControl))
583 *out = &filter->IQualityControl_iface;
584 else if (IsEqualGUID(iid, &IID_IReferenceClock))
585 return IUnknown_QueryInterface(filter->system_clock, iid, out);
586 else
587 return E_NOINTERFACE;
589 IUnknown_AddRef((IUnknown *)*out);
590 return S_OK;
593 static HRESULT dsound_render_init_stream(struct strmbase_filter *iface)
595 struct dsound_render *filter = impl_from_strmbase_filter(iface);
597 if (filter->sink.pin.peer)
598 ResetEvent(filter->state_event);
599 filter->eos = FALSE;
600 ResetEvent(filter->flush_event);
602 return filter->sink.pin.peer ? S_FALSE : S_OK;
605 static HRESULT dsound_render_start_stream(struct strmbase_filter *iface, REFERENCE_TIME start)
607 struct dsound_render *filter = impl_from_strmbase_filter(iface);
609 filter->stream_start = start;
611 SetEvent(filter->state_event);
613 if (filter->sink.pin.peer)
615 filter->eos = FALSE;
616 IDirectSoundBuffer_Play(filter->dsbuffer, 0, 0, DSBPLAY_LOOPING);
619 return S_OK;
622 static HRESULT dsound_render_stop_stream(struct strmbase_filter *iface)
624 struct dsound_render *filter = impl_from_strmbase_filter(iface);
626 if (filter->sink.pin.peer)
628 IDirectSoundBuffer_Stop(filter->dsbuffer);
629 filter->writepos = filter->buf_size;
631 return S_OK;
634 static HRESULT dsound_render_cleanup_stream(struct strmbase_filter *iface)
636 struct dsound_render *filter = impl_from_strmbase_filter(iface);
638 strmbase_passthrough_invalidate_time(&filter->passthrough);
639 SetEvent(filter->state_event);
640 SetEvent(filter->flush_event);
642 return S_OK;
645 static HRESULT dsound_render_wait_state(struct strmbase_filter *iface, DWORD timeout)
647 struct dsound_render *filter = impl_from_strmbase_filter(iface);
649 if (WaitForSingleObject(filter->state_event, timeout) == WAIT_TIMEOUT)
650 return VFW_S_STATE_INTERMEDIATE;
651 return S_OK;
654 static const struct strmbase_filter_ops filter_ops =
656 .filter_destroy = dsound_render_destroy,
657 .filter_get_pin = dsound_render_get_pin,
658 .filter_query_interface = dsound_render_query_interface,
659 .filter_init_stream = dsound_render_init_stream,
660 .filter_start_stream = dsound_render_start_stream,
661 .filter_stop_stream = dsound_render_stop_stream,
662 .filter_cleanup_stream = dsound_render_cleanup_stream,
663 .filter_wait_state = dsound_render_wait_state,
666 /*** IUnknown methods ***/
667 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
668 REFIID riid,
669 LPVOID*ppvObj) {
670 struct dsound_render *This = impl_from_IBasicAudio(iface);
672 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
674 return IUnknown_QueryInterface(This->filter.outer_unk, riid, ppvObj);
677 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
678 struct dsound_render *This = impl_from_IBasicAudio(iface);
680 TRACE("(%p/%p)->()\n", This, iface);
682 return IUnknown_AddRef(This->filter.outer_unk);
685 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
686 struct dsound_render *This = impl_from_IBasicAudio(iface);
688 TRACE("(%p/%p)->()\n", This, iface);
690 return IUnknown_Release(This->filter.outer_unk);
693 HRESULT WINAPI basic_audio_GetTypeInfoCount(IBasicAudio *iface, UINT *count)
695 TRACE("iface %p, count %p.\n", iface, count);
696 *count = 1;
697 return S_OK;
700 HRESULT WINAPI basic_audio_GetTypeInfo(IBasicAudio *iface, UINT index,
701 LCID lcid, ITypeInfo **typeinfo)
703 TRACE("iface %p, index %u, lcid %#x, typeinfo %p.\n", iface, index, lcid, typeinfo);
704 return strmbase_get_typeinfo(IBasicAudio_tid, typeinfo);
707 HRESULT WINAPI basic_audio_GetIDsOfNames(IBasicAudio *iface, REFIID iid,
708 LPOLESTR *names, UINT count, LCID lcid, DISPID *ids)
710 ITypeInfo *typeinfo;
711 HRESULT hr;
713 TRACE("iface %p, iid %s, names %p, count %u, lcid %#x, ids %p.\n",
714 iface, debugstr_guid(iid), names, count, lcid, ids);
716 if (SUCCEEDED(hr = strmbase_get_typeinfo(IBasicAudio_tid, &typeinfo)))
718 hr = ITypeInfo_GetIDsOfNames(typeinfo, names, count, ids);
719 ITypeInfo_Release(typeinfo);
721 return hr;
724 static HRESULT WINAPI basic_audio_Invoke(IBasicAudio *iface, DISPID id, REFIID iid, LCID lcid,
725 WORD flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *error_arg)
727 ITypeInfo *typeinfo;
728 HRESULT hr;
730 TRACE("iface %p, id %d, iid %s, lcid %#x, flags %#x, params %p, result %p, excepinfo %p, error_arg %p.\n",
731 iface, id, debugstr_guid(iid), lcid, flags, params, result, excepinfo, error_arg);
733 if (SUCCEEDED(hr = strmbase_get_typeinfo(IBasicAudio_tid, &typeinfo)))
735 hr = ITypeInfo_Invoke(typeinfo, iface, id, flags, params, result, excepinfo, error_arg);
736 ITypeInfo_Release(typeinfo);
738 return hr;
741 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
742 LONG lVolume) {
743 struct dsound_render *This = impl_from_IBasicAudio(iface);
745 TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
747 if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
748 return E_INVALIDARG;
750 if (This->dsbuffer) {
751 if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
752 return E_FAIL;
755 This->volume = lVolume;
756 return S_OK;
759 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
760 LONG *plVolume) {
761 struct dsound_render *This = impl_from_IBasicAudio(iface);
763 TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
765 if (!plVolume)
766 return E_POINTER;
768 *plVolume = This->volume;
769 return S_OK;
772 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
773 LONG lBalance) {
774 struct dsound_render *This = impl_from_IBasicAudio(iface);
776 TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
778 if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
779 return E_INVALIDARG;
781 if (This->dsbuffer) {
782 if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
783 return E_FAIL;
786 This->pan = lBalance;
787 return S_OK;
790 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
791 LONG *plBalance) {
792 struct dsound_render *This = impl_from_IBasicAudio(iface);
794 TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
796 if (!plBalance)
797 return E_POINTER;
799 *plBalance = This->pan;
800 return S_OK;
803 static const IBasicAudioVtbl IBasicAudio_Vtbl =
805 Basicaudio_QueryInterface,
806 Basicaudio_AddRef,
807 Basicaudio_Release,
808 basic_audio_GetTypeInfoCount,
809 basic_audio_GetTypeInfo,
810 basic_audio_GetIDsOfNames,
811 basic_audio_Invoke,
812 Basicaudio_put_Volume,
813 Basicaudio_get_Volume,
814 Basicaudio_put_Balance,
815 Basicaudio_get_Balance
818 /*** IUnknown methods ***/
819 static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
820 REFIID riid,
821 LPVOID*ppvObj)
823 struct dsound_render *This = impl_from_IAMDirectSound(iface);
825 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
827 return IUnknown_QueryInterface(This->filter.outer_unk, riid, ppvObj);
830 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
832 struct dsound_render *This = impl_from_IAMDirectSound(iface);
834 TRACE("(%p/%p)->()\n", This, iface);
836 return IUnknown_AddRef(This->filter.outer_unk);
839 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
841 struct dsound_render *This = impl_from_IAMDirectSound(iface);
843 TRACE("(%p/%p)->()\n", This, iface);
845 return IUnknown_Release(This->filter.outer_unk);
848 /*** IAMDirectSound methods ***/
849 static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface, IDirectSound **ds)
851 struct dsound_render *This = impl_from_IAMDirectSound(iface);
853 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
855 return E_NOTIMPL;
858 static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
860 struct dsound_render *This = impl_from_IAMDirectSound(iface);
862 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
864 return E_NOTIMPL;
867 static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
869 struct dsound_render *This = impl_from_IAMDirectSound(iface);
871 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
873 return E_NOTIMPL;
876 static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
878 struct dsound_render *This = impl_from_IAMDirectSound(iface);
880 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
882 return E_NOTIMPL;
885 static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
887 struct dsound_render *This = impl_from_IAMDirectSound(iface);
889 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
891 return E_NOTIMPL;
894 static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
896 struct dsound_render *This = impl_from_IAMDirectSound(iface);
898 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
900 return E_NOTIMPL;
903 static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgaudible)
905 struct dsound_render *This = impl_from_IAMDirectSound(iface);
907 FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgaudible);
909 return E_NOTIMPL;
912 static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND *hwnd, BOOL *bgaudible)
914 struct dsound_render *This = impl_from_IAMDirectSound(iface);
916 FIXME("(%p/%p)->(%p,%p): stub\n", This, iface, hwnd, bgaudible);
918 return E_NOTIMPL;
921 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
923 AMDirectSound_QueryInterface,
924 AMDirectSound_AddRef,
925 AMDirectSound_Release,
926 AMDirectSound_GetDirectSoundInterface,
927 AMDirectSound_GetPrimaryBufferInterface,
928 AMDirectSound_GetSecondaryBufferInterface,
929 AMDirectSound_ReleaseDirectSoundInterface,
930 AMDirectSound_ReleasePrimaryBufferInterface,
931 AMDirectSound_ReleaseSecondaryBufferInterface,
932 AMDirectSound_SetFocusWindow,
933 AMDirectSound_GetFocusWindow
936 static struct dsound_render *impl_from_IQualityControl(IQualityControl *iface)
938 return CONTAINING_RECORD(iface, struct dsound_render, IQualityControl_iface);
941 static HRESULT WINAPI dsound_render_qc_QueryInterface(IQualityControl *iface,
942 REFIID iid, void **out)
944 struct dsound_render *filter = impl_from_IQualityControl(iface);
945 return IUnknown_QueryInterface(filter->filter.outer_unk, iid, out);
948 static ULONG WINAPI dsound_render_qc_AddRef(IQualityControl *iface)
950 struct dsound_render *filter = impl_from_IQualityControl(iface);
951 return IUnknown_AddRef(filter->filter.outer_unk);
954 static ULONG WINAPI dsound_render_qc_Release(IQualityControl *iface)
956 struct dsound_render *filter = impl_from_IQualityControl(iface);
957 return IUnknown_AddRef(filter->filter.outer_unk);
960 static HRESULT WINAPI dsound_render_qc_Notify(IQualityControl *iface,
961 IBaseFilter *sender, Quality q)
963 struct dsound_render *filter = impl_from_IQualityControl(iface);
965 FIXME("filter %p, sender %p, type %#x, proportion %u, late %s, timestamp %s, stub!\n",
966 filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp));
968 return E_NOTIMPL;
971 static HRESULT WINAPI dsound_render_qc_SetSink(IQualityControl *iface, IQualityControl *sink)
973 struct dsound_render *filter = impl_from_IQualityControl(iface);
975 FIXME("filter %p, sink %p, stub!\n", filter, sink);
977 return E_NOTIMPL;
980 static const IQualityControlVtbl dsound_render_qc_vtbl =
982 dsound_render_qc_QueryInterface,
983 dsound_render_qc_AddRef,
984 dsound_render_qc_Release,
985 dsound_render_qc_Notify,
986 dsound_render_qc_SetSink,
989 HRESULT dsound_render_create(IUnknown *outer, IUnknown **out)
991 static const DSBUFFERDESC buffer_desc = {
992 .dwSize = sizeof(DSBUFFERDESC),
993 .dwFlags = DSBCAPS_PRIMARYBUFFER,
996 struct dsound_render *object;
997 IDirectSoundBuffer *buffer;
998 HRESULT hr;
1000 if (!(object = calloc(1, sizeof(*object))))
1001 return E_OUTOFMEMORY;
1003 strmbase_filter_init(&object->filter, outer, &CLSID_DSoundRender, &filter_ops);
1005 if (FAILED(hr = system_clock_create(&object->filter.IUnknown_inner, &object->system_clock)))
1007 strmbase_filter_cleanup(&object->filter);
1008 free(object);
1009 return hr;
1012 if (FAILED(hr = DirectSoundCreate8(NULL, &object->dsound, NULL)))
1014 IUnknown_Release(object->system_clock);
1015 strmbase_filter_cleanup(&object->filter);
1016 free(object);
1017 return hr;
1020 if (FAILED(hr = IDirectSound8_SetCooperativeLevel(object->dsound,
1021 GetDesktopWindow(), DSSCL_PRIORITY)))
1023 IDirectSound8_Release(object->dsound);
1024 IUnknown_Release(object->system_clock);
1025 strmbase_filter_cleanup(&object->filter);
1026 free(object);
1027 return hr;
1030 if (SUCCEEDED(hr = IDirectSound8_CreateSoundBuffer(object->dsound,
1031 &buffer_desc, &buffer, NULL)))
1033 IDirectSoundBuffer_Play(buffer, 0, 0, DSBPLAY_LOOPING);
1034 IDirectSoundBuffer_Release(buffer);
1037 strmbase_passthrough_init(&object->passthrough, (IUnknown *)&object->filter.IBaseFilter_iface);
1038 ISeekingPassThru_Init(&object->passthrough.ISeekingPassThru_iface, TRUE, &object->sink.pin.IPin_iface);
1040 strmbase_sink_init(&object->sink, &object->filter, L"Audio Input pin (rendered)", &sink_ops, NULL);
1042 object->state_event = CreateEventW(NULL, TRUE, TRUE, NULL);
1043 object->flush_event = CreateEventW(NULL, TRUE, TRUE, NULL);
1045 object->IBasicAudio_iface.lpVtbl = &IBasicAudio_Vtbl;
1046 object->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl;
1047 object->IQualityControl_iface.lpVtbl = &dsound_render_qc_vtbl;
1049 TRACE("Created DirectSound renderer %p.\n", object);
1050 *out = &object->filter.IUnknown_inner;
1052 return S_OK;