[QUARTZ] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / reactos / dll / directx / wine / quartz / dsoundrender.c
1 /*
2 * Direct Sound Audio Renderer
3 *
4 * Copyright 2004 Christian Costa
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include "quartz_private.h"
22
23 /* NOTE: buffer can still be filled completely,
24 * but we start waiting until only this amount is buffered
25 */
26 static const REFERENCE_TIME DSoundRenderer_Max_Fill = 150 * 10000;
27
28 static const IBaseFilterVtbl DSoundRender_Vtbl;
29 static const IBasicAudioVtbl IBasicAudio_Vtbl;
30 static const IReferenceClockVtbl IReferenceClock_Vtbl;
31 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl;
32 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl;
33
34 typedef struct DSoundRenderImpl
35 {
36 BaseRenderer renderer;
37 BasicAudio basicAudio;
38
39 IReferenceClock IReferenceClock_iface;
40 IAMDirectSound IAMDirectSound_iface;
41 IAMFilterMiscFlags IAMFilterMiscFlags_iface;
42
43 IDirectSound8 *dsound;
44 LPDIRECTSOUNDBUFFER dsbuffer;
45 DWORD buf_size;
46 DWORD in_loop;
47 DWORD last_playpos, writepos;
48
49 REFERENCE_TIME play_time;
50
51 HANDLE blocked;
52
53 LONG volume;
54 LONG pan;
55
56 DWORD threadid;
57 HANDLE advisethread, thread_wait;
58 } DSoundRenderImpl;
59
60 static inline DSoundRenderImpl *impl_from_BaseRenderer(BaseRenderer *iface)
61 {
62 return CONTAINING_RECORD(iface, DSoundRenderImpl, renderer);
63 }
64
65 static inline DSoundRenderImpl *impl_from_IBaseFilter(IBaseFilter *iface)
66 {
67 return CONTAINING_RECORD(iface, DSoundRenderImpl, renderer.filter.IBaseFilter_iface);
68 }
69
70 static inline DSoundRenderImpl *impl_from_IBasicAudio(IBasicAudio *iface)
71 {
72 return CONTAINING_RECORD(iface, DSoundRenderImpl, basicAudio.IBasicAudio_iface);
73 }
74
75 static inline DSoundRenderImpl *impl_from_IReferenceClock(IReferenceClock *iface)
76 {
77 return CONTAINING_RECORD(iface, DSoundRenderImpl, IReferenceClock_iface);
78 }
79
80 static inline DSoundRenderImpl *impl_from_IAMDirectSound(IAMDirectSound *iface)
81 {
82 return CONTAINING_RECORD(iface, DSoundRenderImpl, IAMDirectSound_iface);
83 }
84
85 static inline DSoundRenderImpl *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface)
86 {
87 return CONTAINING_RECORD(iface, DSoundRenderImpl, IAMFilterMiscFlags_iface);
88 }
89
90 static REFERENCE_TIME time_from_pos(DSoundRenderImpl *This, DWORD pos) {
91 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat;
92 REFERENCE_TIME ret = 10000000;
93 ret = ret * pos / wfx->nAvgBytesPerSec;
94 return ret;
95 }
96
97 static DWORD pos_from_time(DSoundRenderImpl *This, REFERENCE_TIME time) {
98 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat;
99 REFERENCE_TIME ret = time;
100 ret *= wfx->nAvgBytesPerSec;
101 ret /= 10000000;
102 ret -= ret % wfx->nBlockAlign;
103 return ret;
104 }
105
106 static void DSoundRender_UpdatePositions(DSoundRenderImpl *This, DWORD *seqwritepos, DWORD *minwritepos) {
107 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat;
108 BYTE *buf1, *buf2;
109 DWORD size1, size2, playpos, writepos, old_writepos, old_playpos, adv;
110 BOOL writepos_set = This->writepos < This->buf_size;
111
112 /* Update position and zero */
113 old_writepos = This->writepos;
114 old_playpos = This->last_playpos;
115 if (old_writepos <= old_playpos)
116 old_writepos += This->buf_size;
117
118 IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, &playpos, &writepos);
119 if (old_playpos > playpos) {
120 adv = This->buf_size + playpos - old_playpos;
121 This->play_time += time_from_pos(This, This->buf_size);
122 } else
123 adv = playpos - old_playpos;
124 This->last_playpos = playpos;
125 if (adv) {
126 TRACE("Moving from %u to %u: clearing %u bytes\n", old_playpos, playpos, adv);
127 IDirectSoundBuffer_Lock(This->dsbuffer, old_playpos, adv, (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
128 memset(buf1, wfx->wBitsPerSample == 8 ? 128 : 0, size1);
129 memset(buf2, wfx->wBitsPerSample == 8 ? 128 : 0, size2);
130 IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
131 }
132 *minwritepos = writepos;
133 if (!writepos_set || old_writepos < writepos) {
134 if (writepos_set) {
135 This->writepos = This->buf_size;
136 FIXME("Underrun of data occurred!\n");
137 }
138 *seqwritepos = writepos;
139 } else
140 *seqwritepos = This->writepos;
141 }
142
143 static HRESULT DSoundRender_GetWritePos(DSoundRenderImpl *This, DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip)
144 {
145 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat;
146 DWORD writepos, min_writepos, playpos;
147 REFERENCE_TIME max_lag = 50 * 10000;
148 REFERENCE_TIME min_lag = 25 * 10000;
149 REFERENCE_TIME cur, writepos_t, delta_t;
150
151 DSoundRender_UpdatePositions(This, &writepos, &min_writepos);
152 playpos = This->last_playpos;
153 if (This->renderer.filter.pClock == &This->IReferenceClock_iface) {
154 max_lag = min_lag;
155 cur = This->play_time + time_from_pos(This, playpos);
156 cur -= This->renderer.filter.rtStreamStart;
157 } else if (This->renderer.filter.pClock) {
158 IReferenceClock_GetTime(This->renderer.filter.pClock, &cur);
159 cur -= This->renderer.filter.rtStreamStart;
160 } else
161 write_at = -1;
162
163 if (writepos == min_writepos)
164 max_lag = 0;
165
166 *skip = 0;
167 if (write_at < 0) {
168 *ret_writepos = writepos;
169 goto end;
170 }
171
172 if (writepos >= playpos)
173 writepos_t = cur + time_from_pos(This, writepos - playpos);
174 else
175 writepos_t = cur + time_from_pos(This, This->buf_size + writepos - playpos);
176
177 /* write_at: Starting time of sample */
178 /* cur: current time of play position */
179 /* writepos_t: current time of our pointer play position */
180 delta_t = write_at - writepos_t;
181 if (delta_t >= -max_lag && delta_t <= max_lag) {
182 TRACE("Continuing from old position\n");
183 *ret_writepos = writepos;
184 } else if (delta_t < 0) {
185 REFERENCE_TIME past, min_writepos_t;
186 WARN("Delta too big %i/%i, overwriting old data or even skipping\n", (int)delta_t / 10000, (int)max_lag / 10000);
187 if (min_writepos >= playpos)
188 min_writepos_t = cur + time_from_pos(This, min_writepos - playpos);
189 else
190 min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos);
191 past = min_writepos_t - write_at;
192 if (past >= 0) {
193 DWORD skipbytes = pos_from_time(This, past);
194 WARN("Skipping %u bytes\n", skipbytes);
195 *skip = skipbytes;
196 *ret_writepos = min_writepos;
197 } else {
198 DWORD aheadbytes = pos_from_time(This, -past);
199 WARN("Advancing %u bytes\n", aheadbytes);
200 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
201 }
202 } else /* delta_t > 0 */ {
203 DWORD aheadbytes;
204 WARN("Delta too big %i/%i, too far ahead\n", (int)delta_t / 10000, (int)max_lag / 10000);
205 aheadbytes = pos_from_time(This, delta_t);
206 WARN("Advancing %u bytes\n", aheadbytes);
207 if (delta_t >= DSoundRenderer_Max_Fill)
208 return S_FALSE;
209 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
210 }
211 end:
212 if (playpos > *ret_writepos)
213 *pfree = playpos - *ret_writepos;
214 else if (playpos == *ret_writepos)
215 *pfree = This->buf_size - wfx->nBlockAlign;
216 else
217 *pfree = This->buf_size + playpos - *ret_writepos;
218 if (time_from_pos(This, This->buf_size - *pfree) >= DSoundRenderer_Max_Fill) {
219 TRACE("Blocked: too full %i / %i\n", (int)(time_from_pos(This, This->buf_size - *pfree)/10000), (int)(DSoundRenderer_Max_Fill / 10000));
220 return S_FALSE;
221 }
222 return S_OK;
223 }
224
225 static HRESULT DSoundRender_HandleEndOfStream(DSoundRenderImpl *This)
226 {
227 while (This->renderer.filter.state == State_Running)
228 {
229 DWORD pos1, pos2;
230 DSoundRender_UpdatePositions(This, &pos1, &pos2);
231 if (pos1 == pos2)
232 break;
233
234 This->in_loop = 1;
235 LeaveCriticalSection(&This->renderer.filter.csFilter);
236 LeaveCriticalSection(&This->renderer.csRenderLock);
237 WaitForSingleObject(This->blocked, 10);
238 EnterCriticalSection(&This->renderer.csRenderLock);
239 EnterCriticalSection(&This->renderer.filter.csFilter);
240 This->in_loop = 0;
241 }
242
243 return S_OK;
244 }
245
246 static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* This, REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size)
247 {
248 HRESULT hr;
249
250 while (size && This->renderer.filter.state != State_Stopped) {
251 DWORD writepos, skip = 0, free, size1, size2, ret;
252 BYTE *buf1, *buf2;
253
254 if (This->renderer.filter.state == State_Running)
255 hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
256 else
257 hr = S_FALSE;
258
259 if (hr != S_OK) {
260 This->in_loop = 1;
261 LeaveCriticalSection(&This->renderer.csRenderLock);
262 ret = WaitForSingleObject(This->blocked, 10);
263 EnterCriticalSection(&This->renderer.csRenderLock);
264 This->in_loop = 0;
265 if (This->renderer.pInputPin->flushing ||
266 This->renderer.filter.state == State_Stopped) {
267 return This->renderer.filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE;
268 }
269 if (ret != WAIT_TIMEOUT)
270 ERR("%x\n", ret);
271 continue;
272 }
273 tStart = -1;
274
275 if (skip)
276 FIXME("Sample dropped %u of %u bytes\n", skip, size);
277 if (skip >= size)
278 return S_OK;
279 data += skip;
280 size -= skip;
281
282 hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
283 if (hr != DS_OK) {
284 ERR("Unable to lock sound buffer! (%x)\n", hr);
285 break;
286 }
287 memcpy(buf1, data, size1);
288 if (size2)
289 memcpy(buf2, data+size1, size2);
290 IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
291 This->writepos = (writepos + size1 + size2) % This->buf_size;
292 TRACE("Wrote %u bytes at %u, next at %u - (%u/%u)\n", size1+size2, writepos, This->writepos, free, size);
293 data += size1 + size2;
294 size -= size1 + size2;
295 }
296 return S_OK;
297 }
298
299 static HRESULT WINAPI DSoundRender_ShouldDrawSampleNow(BaseRenderer *This, IMediaSample *pMediaSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime)
300 {
301 /* We time ourselves do not use the base renderers timing */
302 return S_OK;
303 }
304
305
306 static HRESULT WINAPI DSoundRender_PrepareReceive(BaseRenderer *iface, IMediaSample *pSample)
307 {
308 DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
309 HRESULT hr;
310 AM_MEDIA_TYPE *amt;
311
312 if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
313 {
314 AM_MEDIA_TYPE *orig = &This->renderer.pInputPin->pin.mtCurrent;
315 WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
316 WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
317
318 if (origfmt->wFormatTag == newfmt->wFormatTag &&
319 origfmt->nChannels == newfmt->nChannels &&
320 origfmt->nBlockAlign == newfmt->nBlockAlign &&
321 origfmt->wBitsPerSample == newfmt->wBitsPerSample &&
322 origfmt->cbSize == newfmt->cbSize)
323 {
324 if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec)
325 {
326 hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer,
327 newfmt->nSamplesPerSec);
328 if (FAILED(hr))
329 return VFW_E_TYPE_NOT_ACCEPTED;
330 FreeMediaType(orig);
331 CopyMediaType(orig, amt);
332 IMediaSample_SetMediaType(pSample, NULL);
333 }
334 }
335 else
336 return VFW_E_TYPE_NOT_ACCEPTED;
337 }
338 return S_OK;
339 }
340
341 static HRESULT WINAPI DSoundRender_DoRenderSample(BaseRenderer *iface, IMediaSample * pSample)
342 {
343 DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
344 LPBYTE pbSrcStream = NULL;
345 LONG cbSrcStream = 0;
346 REFERENCE_TIME tStart, tStop;
347 HRESULT hr;
348
349 TRACE("%p %p\n", iface, pSample);
350
351 /* Slightly incorrect, Pause completes when a frame is received so we should signal
352 * pause completion here, but for sound playing a single frame doesn't make sense
353 */
354
355 hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
356 if (FAILED(hr))
357 {
358 ERR("Cannot get pointer to sample data (%x)\n", hr);
359 return hr;
360 }
361
362 hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
363 if (FAILED(hr)) {
364 ERR("Cannot get sample time (%x)\n", hr);
365 tStart = tStop = -1;
366 }
367
368 IMediaSample_IsDiscontinuity(pSample);
369
370 if (IMediaSample_IsPreroll(pSample) == S_OK)
371 {
372 TRACE("Preroll!\n");
373 return S_OK;
374 }
375
376 cbSrcStream = IMediaSample_GetActualDataLength(pSample);
377 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
378
379 hr = DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream);
380 if (This->renderer.filter.state == State_Running && This->renderer.filter.pClock && tStart >= 0) {
381 REFERENCE_TIME jitter, now = 0;
382 Quality q;
383 IReferenceClock_GetTime(This->renderer.filter.pClock, &now);
384 jitter = now - This->renderer.filter.rtStreamStart - tStart;
385 if (jitter <= -DSoundRenderer_Max_Fill)
386 jitter += DSoundRenderer_Max_Fill;
387 else if (jitter < 0)
388 jitter = 0;
389 q.Type = (jitter > 0 ? Famine : Flood);
390 q.Proportion = 1000;
391 q.Late = jitter;
392 q.TimeStamp = tStart;
393 IQualityControl_Notify((IQualityControl *)This->renderer.qcimpl, (IBaseFilter*)This, q);
394 }
395 return hr;
396 }
397
398 static HRESULT WINAPI DSoundRender_CheckMediaType(BaseRenderer *iface, const AM_MEDIA_TYPE * pmt)
399 {
400 WAVEFORMATEX* format;
401
402 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
403 return S_FALSE;
404
405 format = (WAVEFORMATEX*)pmt->pbFormat;
406 TRACE("Format = %p\n", format);
407 TRACE("wFormatTag = %x %x\n", format->wFormatTag, WAVE_FORMAT_PCM);
408 TRACE("nChannels = %d\n", format->nChannels);
409 TRACE("nSamplesPerSec = %d\n", format->nSamplesPerSec);
410 TRACE("nAvgBytesPerSec = %d\n", format->nAvgBytesPerSec);
411 TRACE("nBlockAlign = %d\n", format->nBlockAlign);
412 TRACE("wBitsPerSample = %d\n", format->wBitsPerSample);
413
414 if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
415 return S_FALSE;
416
417 return S_OK;
418 }
419
420 static VOID WINAPI DSoundRender_OnStopStreaming(BaseRenderer * iface)
421 {
422 DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
423
424 TRACE("(%p/%p)->()\n", This, iface);
425
426 IDirectSoundBuffer_Stop(This->dsbuffer);
427 This->writepos = This->buf_size;
428 SetEvent(This->blocked);
429 }
430
431 static VOID WINAPI DSoundRender_OnStartStreaming(BaseRenderer * iface)
432 {
433 DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
434
435 TRACE("(%p)\n", This);
436
437 if (This->renderer.pInputPin->pin.pConnectedTo)
438 {
439 if (This->renderer.filter.state == State_Paused)
440 {
441 /* Unblock our thread, state changing from paused to running doesn't need a reset for state change */
442 SetEvent(This->blocked);
443 }
444 IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
445 ResetEvent(This->blocked);
446 }
447 }
448
449 static HRESULT WINAPI DSoundRender_CompleteConnect(BaseRenderer * iface, IPin * pReceivePin)
450 {
451 DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
452 const AM_MEDIA_TYPE * pmt = &This->renderer.pInputPin->pin.mtCurrent;
453 HRESULT hr = S_OK;
454 WAVEFORMATEX *format;
455 DSBUFFERDESC buf_desc;
456
457 TRACE("(%p)->(%p)\n", This, pReceivePin);
458 dump_AM_MEDIA_TYPE(pmt);
459
460 TRACE("MajorType %s\n", debugstr_guid(&pmt->majortype));
461 TRACE("SubType %s\n", debugstr_guid(&pmt->subtype));
462 TRACE("Format %s\n", debugstr_guid(&pmt->formattype));
463 TRACE("Size %d\n", pmt->cbFormat);
464
465 format = (WAVEFORMATEX*)pmt->pbFormat;
466
467 This->buf_size = format->nAvgBytesPerSec;
468
469 memset(&buf_desc,0,sizeof(DSBUFFERDESC));
470 buf_desc.dwSize = sizeof(DSBUFFERDESC);
471 buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
472 DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS |
473 DSBCAPS_GETCURRENTPOSITION2;
474 buf_desc.dwBufferBytes = This->buf_size;
475 buf_desc.lpwfxFormat = format;
476 hr = IDirectSound_CreateSoundBuffer(This->dsound, &buf_desc, &This->dsbuffer, NULL);
477 This->writepos = This->buf_size;
478 if (FAILED(hr))
479 ERR("Can't create sound buffer (%x)\n", hr);
480
481 if (SUCCEEDED(hr))
482 {
483 hr = IDirectSoundBuffer_SetVolume(This->dsbuffer, This->volume);
484 if (FAILED(hr))
485 ERR("Can't set volume to %d (%x)\n", This->volume, hr);
486
487 hr = IDirectSoundBuffer_SetPan(This->dsbuffer, This->pan);
488 if (FAILED(hr))
489 ERR("Can't set pan to %d (%x)\n", This->pan, hr);
490 hr = S_OK;
491 }
492
493 if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED)
494 {
495 if (This->dsbuffer)
496 IDirectSoundBuffer_Release(This->dsbuffer);
497 This->dsbuffer = NULL;
498 }
499
500 return hr;
501 }
502
503 static HRESULT WINAPI DSoundRender_BreakConnect(BaseRenderer* iface)
504 {
505 DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
506
507 TRACE("(%p)->()\n", iface);
508
509 if (This->threadid) {
510 PostThreadMessageW(This->threadid, WM_APP, 0, 0);
511 LeaveCriticalSection(This->renderer.pInputPin->pin.pCritSec);
512 WaitForSingleObject(This->advisethread, INFINITE);
513 EnterCriticalSection(This->renderer.pInputPin->pin.pCritSec);
514 CloseHandle(This->advisethread);
515 }
516 if (This->dsbuffer)
517 IDirectSoundBuffer_Release(This->dsbuffer);
518 This->dsbuffer = NULL;
519
520 return S_OK;
521 }
522
523 static HRESULT WINAPI DSoundRender_EndOfStream(BaseRenderer* iface)
524 {
525 DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
526 HRESULT hr;
527
528 TRACE("(%p)->()\n",iface);
529
530 hr = BaseRendererImpl_EndOfStream(iface);
531 if (hr != S_OK)
532 {
533 ERR("%08x\n", hr);
534 return hr;
535 }
536
537 hr = DSoundRender_HandleEndOfStream(This);
538
539 return hr;
540 }
541
542 static HRESULT WINAPI DSoundRender_BeginFlush(BaseRenderer* iface)
543 {
544 DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
545
546 TRACE("\n");
547 BaseRendererImpl_BeginFlush(iface);
548 SetEvent(This->blocked);
549
550 return S_OK;
551 }
552
553 static HRESULT WINAPI DSoundRender_EndFlush(BaseRenderer* iface)
554 {
555 DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
556
557 TRACE("\n");
558
559 BaseRendererImpl_EndFlush(iface);
560 if (This->renderer.filter.state != State_Stopped)
561 ResetEvent(This->blocked);
562
563 if (This->dsbuffer)
564 {
565 LPBYTE buffer;
566 DWORD size;
567
568 /* Force a reset */
569 IDirectSoundBuffer_Lock(This->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
570 memset(buffer, 0, size);
571 IDirectSoundBuffer_Unlock(This->dsbuffer, buffer, size, NULL, 0);
572 This->writepos = This->buf_size;
573 }
574
575 return S_OK;
576 }
577
578 static const BaseRendererFuncTable BaseFuncTable = {
579 DSoundRender_CheckMediaType,
580 DSoundRender_DoRenderSample,
581 /**/
582 NULL,
583 NULL,
584 NULL,
585 DSoundRender_OnStartStreaming,
586 DSoundRender_OnStopStreaming,
587 NULL,
588 NULL,
589 NULL,
590 DSoundRender_ShouldDrawSampleNow,
591 DSoundRender_PrepareReceive,
592 /**/
593 DSoundRender_CompleteConnect,
594 DSoundRender_BreakConnect,
595 DSoundRender_EndOfStream,
596 DSoundRender_BeginFlush,
597 DSoundRender_EndFlush,
598 };
599
600 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv)
601 {
602 HRESULT hr;
603 DSoundRenderImpl * pDSoundRender;
604
605 TRACE("(%p, %p)\n", pUnkOuter, ppv);
606
607 *ppv = NULL;
608
609 if (pUnkOuter)
610 return CLASS_E_NOAGGREGATION;
611
612 pDSoundRender = CoTaskMemAlloc(sizeof(DSoundRenderImpl));
613 if (!pDSoundRender)
614 return E_OUTOFMEMORY;
615 ZeroMemory(pDSoundRender, sizeof(DSoundRenderImpl));
616
617 hr = BaseRenderer_Init(&pDSoundRender->renderer, &DSoundRender_Vtbl, (IUnknown*)pDSoundRender, &CLSID_DSoundRender, (DWORD_PTR)(__FILE__ ": DSoundRenderImpl.csFilter"), &BaseFuncTable);
618
619 BasicAudio_Init(&pDSoundRender->basicAudio,&IBasicAudio_Vtbl);
620 pDSoundRender->IReferenceClock_iface.lpVtbl = &IReferenceClock_Vtbl;
621 pDSoundRender->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl;
622 pDSoundRender->IAMFilterMiscFlags_iface.lpVtbl = &IAMFilterMiscFlags_Vtbl;
623
624 if (SUCCEEDED(hr))
625 {
626 hr = DirectSoundCreate8(NULL, &pDSoundRender->dsound, NULL);
627 if (FAILED(hr))
628 ERR("Cannot create Direct Sound object (%x)\n", hr);
629 else
630 hr = IDirectSound_SetCooperativeLevel(pDSoundRender->dsound, GetDesktopWindow(), DSSCL_PRIORITY);
631 if (SUCCEEDED(hr)) {
632 IDirectSoundBuffer *buf;
633 DSBUFFERDESC buf_desc;
634 memset(&buf_desc,0,sizeof(DSBUFFERDESC));
635 buf_desc.dwSize = sizeof(DSBUFFERDESC);
636 buf_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
637 hr = IDirectSound_CreateSoundBuffer(pDSoundRender->dsound, &buf_desc, &buf, NULL);
638 if (SUCCEEDED(hr)) {
639 IDirectSoundBuffer_Play(buf, 0, 0, DSBPLAY_LOOPING);
640 IDirectSoundBuffer_Release(buf);
641 }
642 hr = S_OK;
643 }
644 }
645
646 if (SUCCEEDED(hr))
647 {
648 pDSoundRender->blocked = CreateEventW(NULL, TRUE, TRUE, NULL);
649
650 if (!pDSoundRender->blocked || FAILED(hr))
651 {
652 IBaseFilter_Release(&pDSoundRender->renderer.filter.IBaseFilter_iface);
653 return HRESULT_FROM_WIN32(GetLastError());
654 }
655
656 *ppv = pDSoundRender;
657 }
658 else
659 {
660 BaseRendererImpl_Release(&pDSoundRender->renderer.filter.IBaseFilter_iface);
661 CoTaskMemFree(pDSoundRender);
662 }
663
664 return hr;
665 }
666
667 static HRESULT WINAPI DSoundRender_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
668 {
669 DSoundRenderImpl *This = impl_from_IBaseFilter(iface);
670 TRACE("(%p, %p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
671
672 *ppv = NULL;
673
674 if (IsEqualIID(riid, &IID_IBasicAudio))
675 *ppv = &This->basicAudio.IBasicAudio_iface;
676 else if (IsEqualIID(riid, &IID_IReferenceClock))
677 *ppv = &This->IReferenceClock_iface;
678 else if (IsEqualIID(riid, &IID_IAMDirectSound))
679 *ppv = &This->IAMDirectSound_iface;
680 else if (IsEqualIID(riid, &IID_IAMFilterMiscFlags))
681 *ppv = &This->IAMFilterMiscFlags_iface;
682 else
683 {
684 HRESULT hr;
685 hr = BaseRendererImpl_QueryInterface(iface, riid, ppv);
686 if (SUCCEEDED(hr))
687 return hr;
688 }
689
690 if (*ppv)
691 {
692 IUnknown_AddRef((IUnknown *)(*ppv));
693 return S_OK;
694 }
695
696 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
697 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
698
699 return E_NOINTERFACE;
700 }
701
702 static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface)
703 {
704 DSoundRenderImpl *This = impl_from_IBaseFilter(iface);
705 ULONG refCount = BaseRendererImpl_Release(iface);
706
707 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
708
709 if (!refCount)
710 {
711 if (This->threadid) {
712 PostThreadMessageW(This->threadid, WM_APP, 0, 0);
713 WaitForSingleObject(This->advisethread, INFINITE);
714 CloseHandle(This->advisethread);
715 }
716
717 if (This->dsbuffer)
718 IDirectSoundBuffer_Release(This->dsbuffer);
719 This->dsbuffer = NULL;
720 if (This->dsound)
721 IDirectSound_Release(This->dsound);
722 This->dsound = NULL;
723
724 BasicAudio_Destroy(&This->basicAudio);
725 CloseHandle(This->blocked);
726
727 TRACE("Destroying Audio Renderer\n");
728 CoTaskMemFree(This);
729
730 return 0;
731 }
732 else
733 return refCount;
734 }
735
736 static HRESULT WINAPI DSoundRender_Pause(IBaseFilter * iface)
737 {
738 HRESULT hr = S_OK;
739 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
740
741 TRACE("(%p/%p)->()\n", This, iface);
742
743 EnterCriticalSection(&This->renderer.csRenderLock);
744 if (This->renderer.filter.state != State_Paused)
745 {
746 if (This->renderer.filter.state == State_Stopped)
747 {
748 if (This->renderer.pInputPin->pin.pConnectedTo)
749 ResetEvent(This->renderer.evComplete);
750 This->renderer.pInputPin->end_of_stream = 0;
751 }
752
753 hr = IDirectSoundBuffer_Stop(This->dsbuffer);
754 if (SUCCEEDED(hr))
755 This->renderer.filter.state = State_Paused;
756
757 ResetEvent(This->blocked);
758 ResetEvent(This->renderer.RenderEvent);
759 }
760 ResetEvent(This->renderer.ThreadSignal);
761 LeaveCriticalSection(&This->renderer.csRenderLock);
762
763 return hr;
764 }
765
766 static const IBaseFilterVtbl DSoundRender_Vtbl =
767 {
768 DSoundRender_QueryInterface,
769 BaseFilterImpl_AddRef,
770 DSoundRender_Release,
771 BaseFilterImpl_GetClassID,
772 BaseRendererImpl_Stop,
773 DSoundRender_Pause,
774 BaseRendererImpl_Run,
775 BaseRendererImpl_GetState,
776 BaseRendererImpl_SetSyncSource,
777 BaseFilterImpl_GetSyncSource,
778 BaseFilterImpl_EnumPins,
779 BaseRendererImpl_FindPin,
780 BaseFilterImpl_QueryFilterInfo,
781 BaseFilterImpl_JoinFilterGraph,
782 BaseFilterImpl_QueryVendorInfo
783 };
784
785 /*** IUnknown methods ***/
786 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
787 REFIID riid,
788 LPVOID*ppvObj) {
789 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
790
791 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
792
793 return DSoundRender_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppvObj);
794 }
795
796 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
797 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
798
799 TRACE("(%p/%p)->()\n", This, iface);
800
801 return BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface);
802 }
803
804 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
805 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
806
807 TRACE("(%p/%p)->()\n", This, iface);
808
809 return DSoundRender_Release(&This->renderer.filter.IBaseFilter_iface);
810 }
811
812 /*** IBasicAudio methods ***/
813 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
814 LONG lVolume) {
815 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
816
817 TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
818
819 if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
820 return E_INVALIDARG;
821
822 if (This->dsbuffer) {
823 if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
824 return E_FAIL;
825 }
826
827 This->volume = lVolume;
828 return S_OK;
829 }
830
831 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
832 LONG *plVolume) {
833 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
834
835 TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
836
837 if (!plVolume)
838 return E_POINTER;
839
840 *plVolume = This->volume;
841 return S_OK;
842 }
843
844 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
845 LONG lBalance) {
846 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
847
848 TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
849
850 if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
851 return E_INVALIDARG;
852
853 if (This->dsbuffer) {
854 if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
855 return E_FAIL;
856 }
857
858 This->pan = lBalance;
859 return S_OK;
860 }
861
862 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
863 LONG *plBalance) {
864 DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
865
866 TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
867
868 if (!plBalance)
869 return E_POINTER;
870
871 *plBalance = This->pan;
872 return S_OK;
873 }
874
875 static const IBasicAudioVtbl IBasicAudio_Vtbl =
876 {
877 Basicaudio_QueryInterface,
878 Basicaudio_AddRef,
879 Basicaudio_Release,
880 BasicAudioImpl_GetTypeInfoCount,
881 BasicAudioImpl_GetTypeInfo,
882 BasicAudioImpl_GetIDsOfNames,
883 BasicAudioImpl_Invoke,
884 Basicaudio_put_Volume,
885 Basicaudio_get_Volume,
886 Basicaudio_put_Balance,
887 Basicaudio_get_Balance
888 };
889
890 struct dsoundrender_timer {
891 struct dsoundrender_timer *next;
892 REFERENCE_TIME start;
893 REFERENCE_TIME periodicity;
894 HANDLE handle;
895 DWORD cookie;
896 };
897 static LONG cookie_counter = 1;
898
899 static DWORD WINAPI DSoundAdviseThread(LPVOID lpParam) {
900 DSoundRenderImpl *This = lpParam;
901 struct dsoundrender_timer head = {NULL};
902 MSG msg;
903
904 TRACE("(%p): Main Loop\n", This);
905
906 PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
907 SetEvent(This->thread_wait);
908
909 while (1)
910 {
911 HRESULT hr;
912 REFERENCE_TIME curtime = 0;
913 BOOL ret;
914 struct dsoundrender_timer *prev = &head, *cur;
915
916 hr = IReferenceClock_GetTime(&This->IReferenceClock_iface, &curtime);
917 if (SUCCEEDED(hr)) {
918 TRACE("Time: %s\n", wine_dbgstr_longlong(curtime));
919 while (prev->next) {
920 cur = prev->next;
921 if (cur->start > curtime) {
922 TRACE("Skipping %p\n", cur);
923 prev = cur;
924 } else if (cur->periodicity) {
925 while (cur->start <= curtime) {
926 cur->start += cur->periodicity;
927 ReleaseSemaphore(cur->handle, 1, NULL);
928 }
929 prev = cur;
930 } else {
931 struct dsoundrender_timer *next = cur->next;
932 TRACE("Firing %p %s < %s\n", cur, wine_dbgstr_longlong(cur->start), wine_dbgstr_longlong(curtime));
933 SetEvent(cur->handle);
934 HeapFree(GetProcessHeap(), 0, cur);
935 prev->next = next;
936 }
937 }
938 }
939 if (!head.next)
940 ret = GetMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4);
941 else
942 ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE);
943 while (ret) {
944 switch (LOWORD(msg.message) - WM_APP) {
945 case 0: TRACE("Exiting\n"); return 0;
946 case 1:
947 case 2: {
948 struct dsoundrender_timer *t = (struct dsoundrender_timer *)msg.wParam;
949 if (LOWORD(msg.message) - WM_APP == 1)
950 TRACE("Adding one-shot timer %p\n", t);
951 else
952 TRACE("Adding periodic timer %p\n", t);
953 t->next = head.next;
954 head.next = t;
955 break;
956 }
957 case 3:
958 prev = &head;
959 while (prev->next) {
960 cur = prev->next;
961 if (cur->cookie == msg.wParam) {
962 struct dsoundrender_timer *next = cur->next;
963 HeapFree(GetProcessHeap(), 0, cur);
964 prev->next = next;
965 break;
966 }
967 prev = cur;
968 }
969 break;
970 }
971 ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE);
972 }
973 MsgWaitForMultipleObjects(0, NULL, 5, QS_POSTMESSAGE, 0);
974 }
975 return 0;
976 }
977
978 /*** IUnknown methods ***/
979 static HRESULT WINAPI ReferenceClock_QueryInterface(IReferenceClock *iface,
980 REFIID riid,
981 LPVOID*ppvObj)
982 {
983 DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
984
985 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
986
987 return DSoundRender_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppvObj);
988 }
989
990 static ULONG WINAPI ReferenceClock_AddRef(IReferenceClock *iface)
991 {
992 DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
993
994 TRACE("(%p/%p)->()\n", This, iface);
995
996 return BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface);
997 }
998
999 static ULONG WINAPI ReferenceClock_Release(IReferenceClock *iface)
1000 {
1001 DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1002
1003 TRACE("(%p/%p)->()\n", This, iface);
1004
1005 return DSoundRender_Release(&This->renderer.filter.IBaseFilter_iface);
1006 }
1007
1008 /*** IReferenceClock methods ***/
1009 static HRESULT WINAPI ReferenceClock_GetTime(IReferenceClock *iface,
1010 REFERENCE_TIME *pTime)
1011 {
1012 DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1013 HRESULT hr = E_FAIL;
1014
1015 TRACE("(%p/%p)->(%p)\n", This, iface, pTime);
1016 if (!pTime)
1017 return E_POINTER;
1018
1019 if (This->dsbuffer) {
1020 DWORD writepos1, writepos2;
1021 EnterCriticalSection(&This->renderer.filter.csFilter);
1022 DSoundRender_UpdatePositions(This, &writepos1, &writepos2);
1023 if (This->renderer.pInputPin && This->renderer.pInputPin->pin.mtCurrent.pbFormat)
1024 {
1025 *pTime = This->play_time + time_from_pos(This, This->last_playpos);
1026 hr = S_OK;
1027 }
1028 else
1029 {
1030 ERR("pInputPin Disconnected\n");
1031 hr = E_FAIL;
1032 }
1033 LeaveCriticalSection(&This->renderer.filter.csFilter);
1034 }
1035 if (FAILED(hr))
1036 WARN("Could not get reference time (%x)!\n", hr);
1037
1038 return hr;
1039 }
1040
1041 static HRESULT WINAPI ReferenceClock_AdviseTime(IReferenceClock *iface,
1042 REFERENCE_TIME rtBaseTime,
1043 REFERENCE_TIME rtStreamTime,
1044 HEVENT hEvent,
1045 DWORD_PTR *pdwAdviseCookie)
1046 {
1047 DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1048 REFERENCE_TIME when = rtBaseTime + rtStreamTime;
1049 REFERENCE_TIME future;
1050 TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hEvent, pdwAdviseCookie);
1051
1052 if (when <= 0)
1053 return E_INVALIDARG;
1054
1055 if (!pdwAdviseCookie)
1056 return E_POINTER;
1057
1058 EnterCriticalSection(&This->renderer.filter.csFilter);
1059 future = when - This->play_time;
1060 if (!This->threadid && This->dsbuffer) {
1061 This->thread_wait = CreateEventW(0, 0, 0, 0);
1062 This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid);
1063 WaitForSingleObject(This->thread_wait, INFINITE);
1064 CloseHandle(This->thread_wait);
1065 }
1066 LeaveCriticalSection(&This->renderer.filter.csFilter);
1067 /* If it's in the past or the next millisecond, trigger immediately */
1068 if (future <= 10000) {
1069 SetEvent((HANDLE)hEvent);
1070 *pdwAdviseCookie = 0;
1071 } else {
1072 struct dsoundrender_timer *t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t));
1073 t->next = NULL;
1074 t->start = when;
1075 t->periodicity = 0;
1076 t->handle = (HANDLE)hEvent;
1077 t->cookie = InterlockedIncrement(&cookie_counter);
1078 PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0);
1079 *pdwAdviseCookie = t->cookie;
1080 }
1081
1082 return S_OK;
1083 }
1084
1085 static HRESULT WINAPI ReferenceClock_AdvisePeriodic(IReferenceClock *iface,
1086 REFERENCE_TIME rtStartTime,
1087 REFERENCE_TIME rtPeriodTime,
1088 HSEMAPHORE hSemaphore,
1089 DWORD_PTR *pdwAdviseCookie)
1090 {
1091 DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1092 struct dsoundrender_timer *t;
1093
1094 TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtStartTime), wine_dbgstr_longlong(rtPeriodTime), (void*)hSemaphore, pdwAdviseCookie);
1095
1096 if (rtStartTime <= 0 || rtPeriodTime <= 0)
1097 return E_INVALIDARG;
1098
1099 if (!pdwAdviseCookie)
1100 return E_POINTER;
1101
1102 EnterCriticalSection(&This->renderer.filter.csFilter);
1103 if (!This->threadid && This->dsbuffer) {
1104 This->thread_wait = CreateEventW(0, 0, 0, 0);
1105 This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid);
1106 WaitForSingleObject(This->thread_wait, INFINITE);
1107 CloseHandle(This->thread_wait);
1108 }
1109 LeaveCriticalSection(&This->renderer.filter.csFilter);
1110
1111 t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t));
1112 t->next = NULL;
1113 t->start = rtStartTime;
1114 t->periodicity = rtPeriodTime;
1115 t->handle = (HANDLE)hSemaphore;
1116 t->cookie = InterlockedIncrement(&cookie_counter);
1117 PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0);
1118 *pdwAdviseCookie = t->cookie;
1119
1120 return S_OK;
1121 }
1122
1123 static HRESULT WINAPI ReferenceClock_Unadvise(IReferenceClock *iface,
1124 DWORD_PTR dwAdviseCookie)
1125 {
1126 DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1127
1128 TRACE("(%p/%p)->(%p)\n", This, iface, (void*)dwAdviseCookie);
1129 if (!This->advisethread || !dwAdviseCookie)
1130 return S_FALSE;
1131 PostThreadMessageW(This->threadid, WM_APP+3, dwAdviseCookie, 0);
1132 return S_OK;
1133 }
1134
1135 static const IReferenceClockVtbl IReferenceClock_Vtbl =
1136 {
1137 ReferenceClock_QueryInterface,
1138 ReferenceClock_AddRef,
1139 ReferenceClock_Release,
1140 ReferenceClock_GetTime,
1141 ReferenceClock_AdviseTime,
1142 ReferenceClock_AdvisePeriodic,
1143 ReferenceClock_Unadvise
1144 };
1145
1146 /*** IUnknown methods ***/
1147 static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
1148 REFIID riid,
1149 LPVOID*ppvObj)
1150 {
1151 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1152
1153 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
1154
1155 return DSoundRender_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppvObj);
1156 }
1157
1158 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
1159 {
1160 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1161
1162 TRACE("(%p/%p)->()\n", This, iface);
1163
1164 return BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface);
1165 }
1166
1167 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
1168 {
1169 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1170
1171 TRACE("(%p/%p)->()\n", This, iface);
1172
1173 return DSoundRender_Release(&This->renderer.filter.IBaseFilter_iface);
1174 }
1175
1176 /*** IAMDirectSound methods ***/
1177 static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface, IDirectSound **ds)
1178 {
1179 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1180
1181 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1182
1183 return E_NOTIMPL;
1184 }
1185
1186 static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1187 {
1188 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1189
1190 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1191
1192 return E_NOTIMPL;
1193 }
1194
1195 static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1196 {
1197 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1198
1199 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1200
1201 return E_NOTIMPL;
1202 }
1203
1204 static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
1205 {
1206 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1207
1208 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1209
1210 return E_NOTIMPL;
1211 }
1212
1213 static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1214 {
1215 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1216
1217 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1218
1219 return E_NOTIMPL;
1220 }
1221
1222 static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1223 {
1224 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1225
1226 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1227
1228 return E_NOTIMPL;
1229 }
1230
1231 static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgaudible)
1232 {
1233 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1234
1235 FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgaudible);
1236
1237 return E_NOTIMPL;
1238 }
1239
1240 static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND *hwnd, BOOL *bgaudible)
1241 {
1242 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1243
1244 FIXME("(%p/%p)->(%p,%p): stub\n", This, iface, hwnd, bgaudible);
1245
1246 return E_NOTIMPL;
1247 }
1248
1249 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
1250 {
1251 AMDirectSound_QueryInterface,
1252 AMDirectSound_AddRef,
1253 AMDirectSound_Release,
1254 AMDirectSound_GetDirectSoundInterface,
1255 AMDirectSound_GetPrimaryBufferInterface,
1256 AMDirectSound_GetSecondaryBufferInterface,
1257 AMDirectSound_ReleaseDirectSoundInterface,
1258 AMDirectSound_ReleasePrimaryBufferInterface,
1259 AMDirectSound_ReleaseSecondaryBufferInterface,
1260 AMDirectSound_SetFocusWindow,
1261 AMDirectSound_GetFocusWindow
1262 };
1263
1264 static HRESULT WINAPI AMFilterMiscFlags_QueryInterface(IAMFilterMiscFlags *iface, REFIID riid, void **ppv) {
1265 DSoundRenderImpl *This = impl_from_IAMFilterMiscFlags(iface);
1266 return IBaseFilter_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppv);
1267 }
1268
1269 static ULONG WINAPI AMFilterMiscFlags_AddRef(IAMFilterMiscFlags *iface) {
1270 DSoundRenderImpl *This = impl_from_IAMFilterMiscFlags(iface);
1271 return IBaseFilter_AddRef(&This->renderer.filter.IBaseFilter_iface);
1272 }
1273
1274 static ULONG WINAPI AMFilterMiscFlags_Release(IAMFilterMiscFlags *iface) {
1275 DSoundRenderImpl *This = impl_from_IAMFilterMiscFlags(iface);
1276 return IBaseFilter_Release(&This->renderer.filter.IBaseFilter_iface);
1277 }
1278
1279 static ULONG WINAPI AMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags *iface) {
1280 return AM_FILTER_MISC_FLAGS_IS_RENDERER;
1281 }
1282
1283 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl = {
1284 AMFilterMiscFlags_QueryInterface,
1285 AMFilterMiscFlags_AddRef,
1286 AMFilterMiscFlags_Release,
1287 AMFilterMiscFlags_GetMiscFlags
1288 };