dmloader: Don't claim partial success when loading fails.
[wine] / dlls / 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 "config.h"
22
23 #include "quartz_private.h"
24 #include "pin.h"
25
26 #include "uuids.h"
27 #include "vfwmsgs.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "dshow.h"
31 #include "evcode.h"
32 #include "strmif.h"
33 #include "dsound.h"
34 #include "amaudio.h"
35
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
40
41 /* NOTE: buffer can still be filled completely,
42  * but we start waiting until only this amount is buffered
43  */
44 static const REFERENCE_TIME DSoundRenderer_Max_Fill = 150 * 10000;
45
46 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
47
48 static const IBaseFilterVtbl DSoundRender_Vtbl;
49 static const IPinVtbl DSoundRender_InputPin_Vtbl;
50 static const IBasicAudioVtbl IBasicAudio_Vtbl;
51 static const IReferenceClockVtbl IReferenceClock_Vtbl;
52 static const IMediaSeekingVtbl IMediaSeeking_Vtbl;
53 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl;
54 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl;
55 static const IQualityControlVtbl DSoundRender_QualityControl_Vtbl = {
56     QualityControlImpl_QueryInterface,
57     QualityControlImpl_AddRef,
58     QualityControlImpl_Release,
59     QualityControlImpl_Notify,
60     QualityControlImpl_SetSink
61 };
62
63 typedef struct DSoundRenderImpl
64 {
65     BaseFilter filter;
66
67     const IBasicAudioVtbl *IBasicAudio_vtbl;
68     const IReferenceClockVtbl *IReferenceClock_vtbl;
69     const IAMDirectSoundVtbl *IAMDirectSound_vtbl;
70     const IAMFilterMiscFlagsVtbl *IAMFilterMiscFlags_vtbl;
71     IUnknown *seekthru_unk;
72
73     BaseInputPin * pInputPin;
74     QualityControlImpl qcimpl;
75
76     IDirectSound8 *dsound;
77     LPDIRECTSOUNDBUFFER dsbuffer;
78     DWORD buf_size;
79     DWORD in_loop;
80     DWORD last_playpos, writepos;
81
82     REFERENCE_TIME play_time;
83
84     HANDLE state_change, blocked;
85
86     LONG volume;
87     LONG pan;
88
89     DWORD threadid;
90     HANDLE advisethread, thread_wait;
91 } DSoundRenderImpl;
92
93 static REFERENCE_TIME time_from_pos(DSoundRenderImpl *This, DWORD pos) {
94     WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
95     REFERENCE_TIME ret = 10000000;
96     ret = ret * pos / wfx->nAvgBytesPerSec;
97     return ret;
98 }
99
100 static DWORD pos_from_time(DSoundRenderImpl *This, REFERENCE_TIME time) {
101     WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
102     REFERENCE_TIME ret = time;
103     ret *= wfx->nSamplesPerSec;
104     ret /= 10000000;
105     ret *= wfx->nBlockAlign;
106     return ret;
107 }
108
109 static void DSoundRender_UpdatePositions(DSoundRenderImpl *This, DWORD *seqwritepos, DWORD *minwritepos) {
110     WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
111     BYTE *buf1, *buf2;
112     DWORD size1, size2, playpos, writepos, old_writepos, old_playpos, adv;
113     BOOL writepos_set = This->writepos < This->buf_size;
114
115     /* Update position and zero */
116     old_writepos = This->writepos;
117     old_playpos = This->last_playpos;
118     if (old_writepos <= old_playpos)
119         old_writepos += This->buf_size;
120
121     IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, &playpos, &writepos);
122     if (old_playpos > playpos) {
123         adv = This->buf_size + playpos - old_playpos;
124         This->play_time += time_from_pos(This, This->buf_size);
125     } else
126         adv = playpos - old_playpos;
127     This->last_playpos = playpos;
128     if (adv) {
129         TRACE("Moving from %u to %u: clearing %u 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);
134     }
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");
140         }
141         *seqwritepos = writepos;
142     } else
143         *seqwritepos = This->writepos;
144 }
145
146 static HRESULT DSoundRender_GetWritePos(DSoundRenderImpl *This, DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip)
147 {
148     WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
149     DWORD writepos, min_writepos, playpos;
150     REFERENCE_TIME max_lag = 50 * 10000;
151     REFERENCE_TIME min_lag = 25 * 10000;
152     REFERENCE_TIME cur, writepos_t, delta_t;
153
154     DSoundRender_UpdatePositions(This, &writepos, &min_writepos);
155     playpos = This->last_playpos;
156     if (This->filter.pClock == (IReferenceClock*)&This->IReferenceClock_vtbl) {
157         max_lag = min_lag;
158         cur = This->play_time + time_from_pos(This, playpos);
159         cur -= This->filter.rtStreamStart;
160     } else if (This->filter.pClock) {
161         IReferenceClock_GetTime(This->filter.pClock, &cur);
162         cur -= This->filter.rtStreamStart;
163     } else
164         write_at = -1;
165
166     if (writepos == min_writepos)
167         max_lag = 0;
168
169     *skip = 0;
170     if (write_at < 0) {
171         *ret_writepos = writepos;
172         goto end;
173     }
174
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);
179
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 %i/%i, overwriting old data or even skipping\n", (int)delta_t / 10000, (int)max_lag / 10000);
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;
204         }
205     } else /* delta_t > 0 */ {
206         DWORD aheadbytes;
207         WARN("Delta too big %i/%i, too far ahead\n", (int)delta_t / 10000, (int)max_lag / 10000);
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;
213     }
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 %i / %i\n", (int)(time_from_pos(This, This->buf_size - *pfree)/10000), (int)(DSoundRenderer_Max_Fill / 10000));
223         return S_FALSE;
224     }
225     return S_OK;
226 }
227
228 static HRESULT DSoundRender_HandleEndOfStream(DSoundRenderImpl *This)
229 {
230     HRESULT hr;
231     IMediaEventSink *pEventSink;
232
233     while (1)
234     {
235         DWORD pos1, pos2;
236         DSoundRender_UpdatePositions(This, &pos1, &pos2);
237         if (pos1 == pos2)
238             break;
239
240         This->in_loop = 1;
241         LeaveCriticalSection(&This->filter.csFilter);
242         WaitForSingleObject(This->blocked, 10);
243         EnterCriticalSection(&This->filter.csFilter);
244         This->in_loop = 0;
245         if (This->pInputPin->flushing ||
246             This->filter.state != State_Running) {
247             SetEvent(This->state_change);
248             return S_FALSE;
249         }
250     }
251
252     if (!This->filter.filterInfo.pGraph)
253         return S_OK;
254
255     hr = IFilterGraph_QueryInterface(This->filter.filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
256     if (SUCCEEDED(hr))
257     {
258         hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, (LONG_PTR)This);
259         IMediaEventSink_Release(pEventSink);
260     }
261     return hr;
262 }
263
264 static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* This, REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size)
265 {
266     HRESULT hr;
267
268     while (size && This->filter.state != State_Stopped) {
269         DWORD writepos, skip = 0, free, size1, size2, ret;
270         BYTE *buf1, *buf2;
271
272         if (This->filter.state == State_Running)
273             hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
274         else
275             hr = S_FALSE;
276
277         if (hr != S_OK) {
278             This->in_loop = 1;
279             LeaveCriticalSection(&This->filter.csFilter);
280             ret = WaitForSingleObject(This->blocked, 10);
281             EnterCriticalSection(&This->filter.csFilter);
282             This->in_loop = 0;
283             if (This->pInputPin->flushing ||
284                 This->filter.state == State_Stopped) {
285                 SetEvent(This->state_change);
286                 return This->filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE;
287             }
288             if (ret != WAIT_TIMEOUT)
289                 ERR("%x\n", ret);
290             continue;
291         }
292         tStart = -1;
293
294         if (skip)
295             FIXME("Sample dropped %u of %u bytes\n", skip, size);
296         if (skip >= size)
297             return S_OK;
298         data += skip;
299         size -= skip;
300
301         hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
302         if (hr != DS_OK) {
303             ERR("Unable to lock sound buffer! (%x)\n", hr);
304             break;
305         }
306         memcpy(buf1, data, size1);
307         if (size2)
308             memcpy(buf2, data+size1, size2);
309         IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
310         This->writepos = (writepos + size1 + size2) % This->buf_size;
311         TRACE("Wrote %u bytes at %u, next at %u - (%u/%u)\n", size1+size2, writepos, This->writepos, free, size);
312         data += size1 + size2;
313         size -= size1 + size2;
314     }
315     return S_OK;
316 }
317
318 static HRESULT WINAPI DSoundRender_Receive(BaseInputPin *pin, IMediaSample * pSample)
319 {
320     DSoundRenderImpl *This = (DSoundRenderImpl*)pin->pin.pinInfo.pFilter;
321     LPBYTE pbSrcStream = NULL;
322     LONG cbSrcStream = 0;
323     REFERENCE_TIME tStart, tStop;
324     HRESULT hr;
325     AM_MEDIA_TYPE *amt;
326
327     TRACE("%p %p\n", pin, pSample);
328
329     /* Slightly incorrect, Pause completes when a frame is received so we should signal
330      * pause completion here, but for sound playing a single frame doesn't make sense
331      */
332
333     EnterCriticalSection(&This->filter.csFilter);
334
335     if (This->pInputPin->end_of_stream || This->pInputPin->flushing)
336     {
337         LeaveCriticalSection(&This->filter.csFilter);
338         return S_FALSE;
339     }
340
341     if (This->filter.state == State_Stopped)
342     {
343         LeaveCriticalSection(&This->filter.csFilter);
344         return VFW_E_WRONG_STATE;
345     }
346
347     if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
348     {
349         AM_MEDIA_TYPE *orig = &This->pInputPin->pin.mtCurrent;
350         WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
351         WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
352
353         if (origfmt->wFormatTag == newfmt->wFormatTag &&
354             origfmt->nChannels == newfmt->nChannels &&
355             origfmt->nBlockAlign == newfmt->nBlockAlign &&
356             origfmt->wBitsPerSample == newfmt->wBitsPerSample &&
357             origfmt->cbSize ==  newfmt->cbSize)
358         {
359             if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec)
360             {
361                 hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer,
362                                                      newfmt->nSamplesPerSec);
363                 if (FAILED(hr))
364                 {
365                     LeaveCriticalSection(&This->filter.csFilter);
366                     return VFW_E_TYPE_NOT_ACCEPTED;
367                 }
368                 FreeMediaType(orig);
369                 CopyMediaType(orig, amt);
370                 IMediaSample_SetMediaType(pSample, NULL);
371             }
372         }
373         else
374         {
375             LeaveCriticalSection(&This->filter.csFilter);
376             return VFW_E_TYPE_NOT_ACCEPTED;
377         }
378     }
379
380     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
381     if (FAILED(hr))
382     {
383         ERR("Cannot get pointer to sample data (%x)\n", hr);
384         LeaveCriticalSection(&This->filter.csFilter);
385         return hr;
386     }
387
388     if (IMediaSample_GetMediaTime(pSample, &tStart, &tStop) == S_OK)
389         RendererPosPassThru_RegisterMediaTime(This->seekthru_unk, tStart);
390     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
391     if (FAILED(hr)) {
392         ERR("Cannot get sample time (%x)\n", hr);
393         tStart = tStop = -1;
394     }
395
396     IMediaSample_IsDiscontinuity(pSample);
397
398     if (IMediaSample_IsPreroll(pSample) == S_OK)
399     {
400         TRACE("Preroll!\n");
401         LeaveCriticalSection(&This->filter.csFilter);
402         return S_OK;
403     }
404
405     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
406     TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
407
408     SetEvent(This->state_change);
409     hr = DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream);
410     if (This->filter.state == State_Running && This->filter.pClock && tStart >= 0) {
411         REFERENCE_TIME jitter, now = 0;
412         Quality q;
413         IReferenceClock_GetTime(This->filter.pClock, &now);
414         jitter = now - This->filter.rtStreamStart - tStart;
415         if (jitter <= -DSoundRenderer_Max_Fill)
416             jitter += DSoundRenderer_Max_Fill;
417         else if (jitter < 0)
418             jitter = 0;
419         q.Type = (jitter > 0 ? Famine : Flood);
420         q.Proportion = 1.;
421         q.Late = jitter;
422         q.TimeStamp = tStart;
423         IQualityControl_Notify((IQualityControl *)&This->qcimpl, (IBaseFilter*)This, q);
424     }
425     LeaveCriticalSection(&This->filter.csFilter);
426     return hr;
427 }
428
429 static HRESULT WINAPI DSoundRender_CheckMediaType(BasePin *iface, const AM_MEDIA_TYPE * pmt)
430 {
431     WAVEFORMATEX* format;
432
433     if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
434         return S_FALSE;
435
436     format =  (WAVEFORMATEX*)pmt->pbFormat;
437     TRACE("Format = %p\n", format);
438     TRACE("wFormatTag = %x %x\n", format->wFormatTag, WAVE_FORMAT_PCM);
439     TRACE("nChannels = %d\n", format->nChannels);
440     TRACE("nSamplesPerSec = %d\n", format->nAvgBytesPerSec);
441     TRACE("nAvgBytesPerSec = %d\n", format->nAvgBytesPerSec);
442     TRACE("nBlockAlign = %d\n", format->nBlockAlign);
443     TRACE("wBitsPerSample = %d\n", format->wBitsPerSample);
444
445     if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
446         return S_FALSE;
447
448     return S_OK;
449 }
450
451 static IPin* WINAPI DSoundRender_GetPin(BaseFilter *iface, int pos)
452 {
453     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
454
455     if (pos >= 1 || pos < 0)
456         return NULL;
457
458     IPin_AddRef((IPin*)This->pInputPin);
459     return (IPin*)This->pInputPin;
460 }
461
462 static LONG WINAPI DSoundRender_GetPinCount(BaseFilter *iface)
463 {
464     /* Our pins are static */
465     return 1;
466 }
467
468 static const BaseFilterFuncTable BaseFuncTable = {
469     DSoundRender_GetPin,
470     DSoundRender_GetPinCount
471 };
472
473 static const  BasePinFuncTable input_BaseFuncTable = {
474     DSoundRender_CheckMediaType,
475     NULL,
476     BasePinImpl_GetMediaTypeVersion,
477     BasePinImpl_GetMediaType
478 };
479
480 static const BaseInputPinFuncTable input_BaseInputFuncTable = {
481     DSoundRender_Receive
482 };
483
484
485 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv)
486 {
487     HRESULT hr;
488     PIN_INFO piInput;
489     DSoundRenderImpl * pDSoundRender;
490
491     TRACE("(%p, %p)\n", pUnkOuter, ppv);
492
493     *ppv = NULL;
494
495     if (pUnkOuter)
496         return CLASS_E_NOAGGREGATION;
497     
498     pDSoundRender = CoTaskMemAlloc(sizeof(DSoundRenderImpl));
499     if (!pDSoundRender)
500         return E_OUTOFMEMORY;
501     ZeroMemory(pDSoundRender, sizeof(DSoundRenderImpl));
502
503     BaseFilter_Init(&pDSoundRender->filter, &DSoundRender_Vtbl, &CLSID_DSoundRender, (DWORD_PTR)(__FILE__ ": DSoundRenderImpl.csFilter"), &BaseFuncTable);
504
505     pDSoundRender->IBasicAudio_vtbl = &IBasicAudio_Vtbl;
506     pDSoundRender->IReferenceClock_vtbl = &IReferenceClock_Vtbl;
507     pDSoundRender->IAMDirectSound_vtbl = &IAMDirectSound_Vtbl;
508     pDSoundRender->IAMFilterMiscFlags_vtbl = &IAMFilterMiscFlags_Vtbl;
509
510     /* construct input pin */
511     piInput.dir = PINDIR_INPUT;
512     piInput.pFilter = (IBaseFilter *)pDSoundRender;
513     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
514     hr = BaseInputPin_Construct(&DSoundRender_InputPin_Vtbl, &piInput, &input_BaseFuncTable, &input_BaseInputFuncTable, &pDSoundRender->filter.csFilter, NULL, (IPin **)&pDSoundRender->pInputPin);
515
516     if (SUCCEEDED(hr))
517     {
518         hr = DirectSoundCreate8(NULL, &pDSoundRender->dsound, NULL);
519         if (FAILED(hr))
520             ERR("Cannot create Direct Sound object (%x)\n", hr);
521         else
522             hr = IDirectSound_SetCooperativeLevel(pDSoundRender->dsound, GetDesktopWindow(), DSSCL_PRIORITY);
523         if (SUCCEEDED(hr)) {
524             IDirectSoundBuffer *buf;
525             DSBUFFERDESC buf_desc;
526             memset(&buf_desc,0,sizeof(DSBUFFERDESC));
527             buf_desc.dwSize = sizeof(DSBUFFERDESC);
528             buf_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
529             hr = IDirectSound_CreateSoundBuffer(pDSoundRender->dsound, &buf_desc, &buf, NULL);
530             if (SUCCEEDED(hr)) {
531                 IDirectSoundBuffer_Play(buf, 0, 0, DSBPLAY_LOOPING);
532                 IUnknown_Release(buf);
533             }
534             hr = S_OK;
535         }
536     }
537
538     if (SUCCEEDED(hr))
539     {
540         pDSoundRender->state_change = CreateEventW(NULL, TRUE, TRUE, NULL);
541         pDSoundRender->blocked = CreateEventW(NULL, TRUE, TRUE, NULL);
542
543         hr = CreatePosPassThru((IUnknown*)pDSoundRender, TRUE, (IPin*)pDSoundRender->pInputPin, &pDSoundRender->seekthru_unk);
544
545         if (!pDSoundRender->state_change || !pDSoundRender->blocked || FAILED(hr))
546         {
547             IUnknown_Release((IUnknown *)pDSoundRender);
548             return HRESULT_FROM_WIN32(GetLastError());
549         }
550
551         QualityControlImpl_init(&pDSoundRender->qcimpl, (IPin*)pDSoundRender->pInputPin, (IBaseFilter*)pDSoundRender);
552         pDSoundRender->qcimpl.lpVtbl = &DSoundRender_QualityControl_Vtbl;
553         *ppv = pDSoundRender;
554     }
555     else
556     {
557         if (pDSoundRender->pInputPin)
558             IPin_Release((IPin*)pDSoundRender->pInputPin);
559         BaseFilterImpl_Release((IBaseFilter*)pDSoundRender);
560         CoTaskMemFree(pDSoundRender);
561     }
562
563     return hr;
564 }
565
566 static HRESULT WINAPI DSoundRender_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
567 {
568     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
569     TRACE("(%p, %p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
570
571     *ppv = NULL;
572
573     if (IsEqualIID(riid, &IID_IUnknown))
574         *ppv = This;
575     else if (IsEqualIID(riid, &IID_IPersist))
576         *ppv = This;
577     else if (IsEqualIID(riid, &IID_IMediaFilter))
578         *ppv = This;
579     else if (IsEqualIID(riid, &IID_IBaseFilter))
580         *ppv = This;
581     else if (IsEqualIID(riid, &IID_IBasicAudio))
582         *ppv = &This->IBasicAudio_vtbl;
583     else if (IsEqualIID(riid, &IID_IReferenceClock))
584         *ppv = &This->IReferenceClock_vtbl;
585     else if (IsEqualIID(riid, &IID_IMediaSeeking))
586         return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
587     else if (IsEqualIID(riid, &IID_IAMDirectSound))
588         *ppv = &This->IAMDirectSound_vtbl;
589     else if (IsEqualIID(riid, &IID_IAMFilterMiscFlags))
590         *ppv = &This->IAMFilterMiscFlags_vtbl;
591     else if (IsEqualIID(riid, &IID_IQualityControl))
592         *ppv = &This->qcimpl;
593
594     if (*ppv)
595     {
596         IUnknown_AddRef((IUnknown *)(*ppv));
597         return S_OK;
598     }
599
600     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
601         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
602
603     return E_NOINTERFACE;
604 }
605
606 static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface)
607 {
608     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
609     ULONG refCount = BaseFilterImpl_Release(iface);
610
611     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
612
613     if (!refCount)
614     {
615         IPin *pConnectedTo;
616
617         if (This->threadid) {
618             PostThreadMessageW(This->threadid, WM_APP, 0, 0);
619             WaitForSingleObject(This->advisethread, INFINITE);
620             CloseHandle(This->advisethread);
621         }
622
623         if (This->dsbuffer)
624             IDirectSoundBuffer_Release(This->dsbuffer);
625         This->dsbuffer = NULL;
626         if (This->dsound)
627             IDirectSound_Release(This->dsound);
628         This->dsound = NULL;
629         if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo)))
630         {
631             IPin_Disconnect(pConnectedTo);
632             IPin_Release(pConnectedTo);
633         }
634         IPin_Disconnect((IPin *)This->pInputPin);
635
636         IPin_Release((IPin *)This->pInputPin);
637
638         This->IBasicAudio_vtbl = NULL;
639         if (This->seekthru_unk)
640             IUnknown_Release(This->seekthru_unk);
641
642         CloseHandle(This->state_change);
643         CloseHandle(This->blocked);
644
645         TRACE("Destroying Audio Renderer\n");
646         CoTaskMemFree(This);
647         
648         return 0;
649     }
650     else
651         return refCount;
652 }
653
654 /** IMediaFilter methods **/
655
656 static HRESULT WINAPI DSoundRender_Stop(IBaseFilter * iface)
657 {
658     HRESULT hr = S_OK;
659     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
660
661     TRACE("(%p/%p)->()\n", This, iface);
662
663     EnterCriticalSection(&This->filter.csFilter);
664     {
665         hr = IDirectSoundBuffer_Stop(This->dsbuffer);
666         if (SUCCEEDED(hr))
667             This->filter.state = State_Stopped;
668
669         /* Complete our transition */
670         This->writepos = This->buf_size;
671         SetEvent(This->state_change);
672         SetEvent(This->blocked);
673         RendererPosPassThru_ResetMediaTime(This->seekthru_unk);
674     }
675     LeaveCriticalSection(&This->filter.csFilter);
676     
677     return hr;
678 }
679
680 static HRESULT WINAPI DSoundRender_Pause(IBaseFilter * iface)
681 {
682     HRESULT hr = S_OK;
683     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
684     
685     TRACE("(%p/%p)->()\n", This, iface);
686
687     EnterCriticalSection(&This->filter.csFilter);
688     if (This->filter.state != State_Paused)
689     {
690         if (This->filter.state == State_Stopped)
691         {
692             This->pInputPin->end_of_stream = 0;
693             ResetEvent(This->state_change);
694         }
695
696         hr = IDirectSoundBuffer_Stop(This->dsbuffer);
697         if (SUCCEEDED(hr))
698             This->filter.state = State_Paused;
699
700         ResetEvent(This->blocked);
701     }
702     LeaveCriticalSection(&This->filter.csFilter);
703
704     return hr;
705 }
706
707 static HRESULT WINAPI DSoundRender_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
708 {
709     HRESULT hr = S_OK;
710     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
711
712     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
713
714     EnterCriticalSection(&This->filter.csFilter);
715     if (This->pInputPin->pin.pConnectedTo)
716     {
717         This->filter.rtStreamStart = tStart;
718         QualityControlRender_Start(&This->qcimpl, tStart);
719         if (This->filter.state == State_Paused)
720         {
721             /* Unblock our thread, state changing from paused to running doesn't need a reset for state change */
722             SetEvent(This->blocked);
723         }
724         else if (This->filter.state == State_Stopped)
725         {
726             ResetEvent(This->state_change);
727             This->pInputPin->end_of_stream = 0;
728         }
729         IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
730         ResetEvent(This->blocked);
731     } else if (This->filter.filterInfo.pGraph) {
732         IMediaEventSink *pEventSink;
733         hr = IFilterGraph_QueryInterface(This->filter.filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
734         if (SUCCEEDED(hr))
735         {
736             hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, (LONG_PTR)This);
737             IMediaEventSink_Release(pEventSink);
738         }
739         hr = S_OK;
740     }
741     if (SUCCEEDED(hr))
742         This->filter.state = State_Running;
743     LeaveCriticalSection(&This->filter.csFilter);
744
745     return hr;
746 }
747
748 static HRESULT WINAPI DSoundRender_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
749 {
750     HRESULT hr;
751     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
752
753     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
754
755     if (WaitForSingleObject(This->state_change, dwMilliSecsTimeout) == WAIT_TIMEOUT)
756         hr = VFW_S_STATE_INTERMEDIATE;
757     else
758         hr = S_OK;
759
760     BaseFilterImpl_GetState(iface, dwMilliSecsTimeout, pState);
761
762     return hr;
763 }
764
765 /** IBaseFilter implementation **/
766
767 static HRESULT WINAPI DSoundRender_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
768 {
769     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
770
771     TRACE("(%p/%p)->(%s,%p)\n", This, iface, debugstr_w(Id), ppPin);
772     
773     FIXME("DSoundRender::FindPin(...)\n");
774
775     /* FIXME: critical section */
776
777     return E_NOTIMPL;
778 }
779
780 static const IBaseFilterVtbl DSoundRender_Vtbl =
781 {
782     DSoundRender_QueryInterface,
783     BaseFilterImpl_AddRef,
784     DSoundRender_Release,
785     BaseFilterImpl_GetClassID,
786     DSoundRender_Stop,
787     DSoundRender_Pause,
788     DSoundRender_Run,
789     DSoundRender_GetState,
790     BaseFilterImpl_SetSyncSource,
791     BaseFilterImpl_GetSyncSource,
792     BaseFilterImpl_EnumPins,
793     DSoundRender_FindPin,
794     BaseFilterImpl_QueryFilterInfo,
795     BaseFilterImpl_JoinFilterGraph,
796     BaseFilterImpl_QueryVendorInfo
797 };
798
799 static HRESULT WINAPI DSoundRender_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
800 {
801     BaseInputPin *This = (BaseInputPin *)iface;
802     PIN_DIRECTION pindirReceive;
803     DSoundRenderImpl *DSImpl;
804     HRESULT hr = S_OK;
805
806     TRACE("(%p)->(%p, %p)\n", This, pReceivePin, pmt);
807     dump_AM_MEDIA_TYPE(pmt);
808
809     EnterCriticalSection(This->pin.pCritSec);
810     {
811         DSImpl = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
812
813         if (This->pin.pConnectedTo)
814             hr = VFW_E_ALREADY_CONNECTED;
815
816         if (SUCCEEDED(hr) && This->pin.pFuncsTable->pfnCheckMediaType((BasePin*)This, pmt) != S_OK)
817             hr = VFW_E_TYPE_NOT_ACCEPTED;
818
819         if (SUCCEEDED(hr))
820         {
821             IPin_QueryDirection(pReceivePin, &pindirReceive);
822
823             if (pindirReceive != PINDIR_OUTPUT)
824             {
825                 ERR("Can't connect from non-output pin\n");
826                 hr = VFW_E_INVALID_DIRECTION;
827             }
828         }
829
830         if (SUCCEEDED(hr))
831         {
832             WAVEFORMATEX *format;
833             DSBUFFERDESC buf_desc;
834
835             TRACE("MajorType %s\n", debugstr_guid(&pmt->majortype));
836             TRACE("SubType %s\n", debugstr_guid(&pmt->subtype));
837             TRACE("Format %s\n", debugstr_guid(&pmt->formattype));
838             TRACE("Size %d\n", pmt->cbFormat);
839
840             format = (WAVEFORMATEX*)pmt->pbFormat;
841
842             DSImpl->buf_size = format->nAvgBytesPerSec;
843
844             memset(&buf_desc,0,sizeof(DSBUFFERDESC));
845             buf_desc.dwSize = sizeof(DSBUFFERDESC);
846             buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
847                                DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS |
848                                DSBCAPS_GETCURRENTPOSITION2;
849             buf_desc.dwBufferBytes = DSImpl->buf_size;
850             buf_desc.lpwfxFormat = format;
851             hr = IDirectSound_CreateSoundBuffer(DSImpl->dsound, &buf_desc, &DSImpl->dsbuffer, NULL);
852             DSImpl->writepos = DSImpl->buf_size;
853             if (FAILED(hr))
854                 ERR("Can't create sound buffer (%x)\n", hr);
855         }
856
857         if (SUCCEEDED(hr))
858         {
859             hr = IDirectSoundBuffer_SetVolume(DSImpl->dsbuffer, DSImpl->volume);
860             if (FAILED(hr))
861                 ERR("Can't set volume to %d (%x)\n", DSImpl->volume, hr);
862
863             hr = IDirectSoundBuffer_SetPan(DSImpl->dsbuffer, DSImpl->pan);
864             if (FAILED(hr))
865                 ERR("Can't set pan to %d (%x)\n", DSImpl->pan, hr);
866             hr = S_OK;
867         }
868
869         if (SUCCEEDED(hr))
870         {
871             CopyMediaType(&This->pin.mtCurrent, pmt);
872             This->pin.pConnectedTo = pReceivePin;
873             IPin_AddRef(pReceivePin);
874         }
875         else if (hr != VFW_E_ALREADY_CONNECTED)
876         {
877             if (DSImpl->dsbuffer)
878                 IDirectSoundBuffer_Release(DSImpl->dsbuffer);
879             DSImpl->dsbuffer = NULL;
880         }
881     }
882     LeaveCriticalSection(This->pin.pCritSec);
883
884     return hr;
885 }
886
887 static HRESULT WINAPI DSoundRender_InputPin_Disconnect(IPin * iface)
888 {
889     BasePin *This = (BasePin*)iface;
890     DSoundRenderImpl *DSImpl;
891
892     TRACE("(%p)->()\n", iface);
893
894     DSImpl = (DSoundRenderImpl*)This->pinInfo.pFilter;
895     if (DSImpl->threadid) {
896         PostThreadMessageW(DSImpl->threadid, WM_APP, 0, 0);
897         WaitForSingleObject(DSImpl->advisethread, INFINITE);
898         CloseHandle(DSImpl->advisethread);
899     }
900     if (DSImpl->dsbuffer)
901         IDirectSoundBuffer_Release(DSImpl->dsbuffer);
902     DSImpl->dsbuffer = NULL;
903
904     return BasePinImpl_Disconnect(iface);
905 }
906
907 static HRESULT WINAPI DSoundRender_InputPin_EndOfStream(IPin * iface)
908 {
909     BaseInputPin* This = (BaseInputPin*)iface;
910     DSoundRenderImpl *me = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
911     HRESULT hr;
912
913     EnterCriticalSection(This->pin.pCritSec);
914
915     TRACE("(%p/%p)->()\n", This, iface);
916     hr = BaseInputPinImpl_EndOfStream(iface);
917     if (hr != S_OK)
918     {
919         ERR("%08x\n", hr);
920         LeaveCriticalSection(This->pin.pCritSec);
921         return hr;
922     }
923
924     hr = DSoundRender_HandleEndOfStream(me);
925     RendererPosPassThru_EOS(me->seekthru_unk);
926     SetEvent(me->state_change);
927     LeaveCriticalSection(This->pin.pCritSec);
928
929     return hr;
930 }
931
932 static HRESULT WINAPI DSoundRender_InputPin_BeginFlush(IPin * iface)
933 {
934     BaseInputPin *This = (BaseInputPin *)iface;
935     DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
936     HRESULT hr;
937
938     TRACE("\n");
939
940     EnterCriticalSection(This->pin.pCritSec);
941     hr = BaseInputPinImpl_BeginFlush(iface);
942     SetEvent(pFilter->blocked);
943     LeaveCriticalSection(This->pin.pCritSec);
944
945     return hr;
946 }
947
948 static HRESULT WINAPI DSoundRender_InputPin_EndFlush(IPin * iface)
949 {
950     BaseInputPin *This = (BaseInputPin *)iface;
951     DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
952     HRESULT hr;
953
954     TRACE("\n");
955
956     EnterCriticalSection(This->pin.pCritSec);
957     if (pFilter->in_loop) {
958         ResetEvent(pFilter->state_change);
959         LeaveCriticalSection(This->pin.pCritSec);
960         WaitForSingleObject(pFilter->state_change, -1);
961         EnterCriticalSection(This->pin.pCritSec);
962     }
963     if (pFilter->filter.state != State_Stopped)
964         ResetEvent(pFilter->blocked);
965
966     if (pFilter->dsbuffer)
967     {
968         LPBYTE buffer;
969         DWORD size;
970
971         /* Force a reset */
972         IDirectSoundBuffer_Lock(pFilter->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
973         memset(buffer, 0, size);
974         IDirectSoundBuffer_Unlock(pFilter->dsbuffer, buffer, size, NULL, 0);
975         pFilter->writepos = pFilter->buf_size;
976     }
977     QualityControlRender_Start(&pFilter->qcimpl, pFilter->filter.rtStreamStart);
978     hr = BaseInputPinImpl_EndFlush(iface);
979     LeaveCriticalSection(This->pin.pCritSec);
980     RendererPosPassThru_ResetMediaTime(pFilter->seekthru_unk);
981
982     return hr;
983 }
984
985 static const IPinVtbl DSoundRender_InputPin_Vtbl =
986 {
987     BaseInputPinImpl_QueryInterface,
988     BasePinImpl_AddRef,
989     BaseInputPinImpl_Release,
990     BaseInputPinImpl_Connect,
991     DSoundRender_InputPin_ReceiveConnection,
992     DSoundRender_InputPin_Disconnect,
993     BasePinImpl_ConnectedTo,
994     BasePinImpl_ConnectionMediaType,
995     BasePinImpl_QueryPinInfo,
996     BasePinImpl_QueryDirection,
997     BasePinImpl_QueryId,
998     BaseInputPinImpl_QueryAccept,
999     BasePinImpl_EnumMediaTypes,
1000     BasePinImpl_QueryInternalConnections,
1001     DSoundRender_InputPin_EndOfStream,
1002     DSoundRender_InputPin_BeginFlush,
1003     DSoundRender_InputPin_EndFlush,
1004     BaseInputPinImpl_NewSegment
1005 };
1006
1007 /*** IUnknown methods ***/
1008 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
1009                                                 REFIID riid,
1010                                                 LPVOID*ppvObj) {
1011     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1012
1013     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1014
1015     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1016 }
1017
1018 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
1019     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1020
1021     TRACE("(%p/%p)->()\n", This, iface);
1022
1023     return BaseFilterImpl_AddRef((IBaseFilter*)This);
1024 }
1025
1026 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
1027     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1028
1029     TRACE("(%p/%p)->()\n", This, iface);
1030
1031     return DSoundRender_Release((IBaseFilter*)This);
1032 }
1033
1034 /*** IDispatch methods ***/
1035 static HRESULT WINAPI Basicaudio_GetTypeInfoCount(IBasicAudio *iface,
1036                                                   UINT*pctinfo) {
1037     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1038
1039     TRACE("(%p/%p)->(%p): stub !!!\n", This, iface, pctinfo);
1040
1041     return S_OK;
1042 }
1043
1044 static HRESULT WINAPI Basicaudio_GetTypeInfo(IBasicAudio *iface,
1045                                              UINT iTInfo,
1046                                              LCID lcid,
1047                                              ITypeInfo**ppTInfo) {
1048     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1049
1050     TRACE("(%p/%p)->(%d, %d, %p): stub !!!\n", This, iface, iTInfo, lcid, ppTInfo);
1051
1052     return S_OK;
1053 }
1054
1055 static HRESULT WINAPI Basicaudio_GetIDsOfNames(IBasicAudio *iface,
1056                                                REFIID riid,
1057                                                LPOLESTR*rgszNames,
1058                                                UINT cNames,
1059                                                LCID lcid,
1060                                                DISPID*rgDispId) {
1061     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1062
1063     TRACE("(%p/%p)->(%s (%p), %p, %d, %d, %p): stub !!!\n", This, iface, debugstr_guid(riid), riid, rgszNames, cNames, lcid, rgDispId);
1064
1065     return S_OK;
1066 }
1067
1068 static HRESULT WINAPI Basicaudio_Invoke(IBasicAudio *iface,
1069                                         DISPID dispIdMember,
1070                                         REFIID riid,
1071                                         LCID lcid,
1072                                         WORD wFlags,
1073                                         DISPPARAMS*pDispParams,
1074                                         VARIANT*pVarResult,
1075                                         EXCEPINFO*pExepInfo,
1076                                         UINT*puArgErr) {
1077     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1078
1079     TRACE("(%p/%p)->(%d, %s (%p), %d, %04x, %p, %p, %p, %p): stub !!!\n", This, iface, dispIdMember, debugstr_guid(riid), riid, lcid, wFlags, pDispParams, pVarResult, pExepInfo, puArgErr);
1080
1081     return S_OK;
1082 }
1083
1084 /*** IBasicAudio methods ***/
1085 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
1086                                             LONG lVolume) {
1087     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1088
1089     TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
1090
1091     if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
1092         return E_INVALIDARG;
1093
1094     if (This->dsbuffer) {
1095         if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
1096             return E_FAIL;
1097     }
1098
1099     This->volume = lVolume;
1100     return S_OK;
1101 }
1102
1103 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
1104                                             LONG *plVolume) {
1105     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1106
1107     TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
1108
1109     if (!plVolume)
1110         return E_POINTER;
1111
1112     *plVolume = This->volume;
1113     return S_OK;
1114 }
1115
1116 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
1117                                              LONG lBalance) {
1118     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1119
1120     TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
1121
1122     if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
1123         return E_INVALIDARG;
1124
1125     if (This->dsbuffer) {
1126         if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
1127             return E_FAIL;
1128     }
1129
1130     This->pan = lBalance;
1131     return S_OK;
1132 }
1133
1134 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
1135                                              LONG *plBalance) {
1136     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1137
1138     TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
1139
1140     if (!plBalance)
1141         return E_POINTER;
1142
1143     *plBalance = This->pan;
1144     return S_OK;
1145 }
1146
1147 static const IBasicAudioVtbl IBasicAudio_Vtbl =
1148 {
1149     Basicaudio_QueryInterface,
1150     Basicaudio_AddRef,
1151     Basicaudio_Release,
1152     Basicaudio_GetTypeInfoCount,
1153     Basicaudio_GetTypeInfo,
1154     Basicaudio_GetIDsOfNames,
1155     Basicaudio_Invoke,
1156     Basicaudio_put_Volume,
1157     Basicaudio_get_Volume,
1158     Basicaudio_put_Balance,
1159     Basicaudio_get_Balance
1160 };
1161
1162 struct dsoundrender_timer {
1163     struct dsoundrender_timer *next;
1164     REFERENCE_TIME start;
1165     REFERENCE_TIME periodicity;
1166     HANDLE handle;
1167     DWORD cookie;
1168 };
1169 static LONG cookie_counter = 1;
1170
1171 static DWORD WINAPI DSoundAdviseThread(LPVOID lpParam) {
1172     DSoundRenderImpl *This = lpParam;
1173     struct dsoundrender_timer head = { };
1174     MSG msg;
1175
1176     TRACE("(%p): Main Loop\n", This);
1177
1178     PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
1179     SetEvent(This->thread_wait);
1180
1181     while (1)
1182     {
1183         HRESULT hr;
1184         REFERENCE_TIME curtime = 0;
1185         BOOL ret;
1186         struct dsoundrender_timer *prev = &head, *cur;
1187
1188         hr = IReferenceClock_GetTime((IReferenceClock*) &This->IReferenceClock_vtbl, &curtime);
1189         if (FAILED(hr)) {
1190             FIXME("Could not get time: %08x\n", hr);
1191             continue;
1192         }
1193         TRACE("Time: %s\n", wine_dbgstr_longlong(curtime));
1194         while (prev->next) {
1195             cur = prev->next;
1196             if (cur->start > curtime) {
1197                 TRACE("Skipping %p\n", cur);
1198                 prev = cur;
1199             } else if (cur->periodicity) {
1200                 while (cur->start <= curtime) {
1201                     cur->start += cur->periodicity;
1202                     ReleaseSemaphore(cur->handle, 1, NULL);
1203                 }
1204                 prev = cur;
1205             } else {
1206                 struct dsoundrender_timer *next = cur->next;
1207                 TRACE("Firing %p %s < %s\n", cur, wine_dbgstr_longlong(cur->start), wine_dbgstr_longlong(curtime));
1208                 SetEvent(cur->handle);
1209                 HeapFree(GetProcessHeap(), 0, cur);
1210                 prev->next = next;
1211             }
1212         }
1213         if (!head.next)
1214             ret = GetMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4);
1215         else
1216             ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE);
1217         while (ret) {
1218             switch (LOWORD(msg.message) - WM_APP) {
1219                 case 0: TRACE("Exiting\n"); return 0;
1220                 case 1:
1221                 case 2: {
1222                     struct dsoundrender_timer *t = (struct dsoundrender_timer *)msg.wParam;
1223                     if (LOWORD(msg.message) - WM_APP == 1)
1224                         TRACE("Adding one-shot timer %p\n", t);
1225                     else
1226                         TRACE("Adding periodic timer %p\n", t);
1227                     t->next = head.next;
1228                     head.next = t;
1229                     break;
1230                 }
1231                 case 3:
1232                     prev = &head;
1233                     while (prev->next) {
1234                         cur = prev->next;
1235                         if (cur->cookie == msg.wParam) {
1236                             struct dsoundrender_timer *next = cur->next;
1237                             HeapFree(GetProcessHeap(), 0, cur);
1238                             prev->next = next;
1239                             break;
1240                         }
1241                         prev = cur;
1242                     }
1243                     break;
1244             }
1245             ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE);
1246         }
1247         MsgWaitForMultipleObjects(0, NULL, 5, QS_POSTMESSAGE, 0);
1248     }
1249     return 0;
1250 }
1251
1252 /*** IUnknown methods ***/
1253 static HRESULT WINAPI ReferenceClock_QueryInterface(IReferenceClock *iface,
1254                                                 REFIID riid,
1255                                                 LPVOID*ppvObj)
1256 {
1257     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1258
1259     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1260
1261     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1262 }
1263
1264 static ULONG WINAPI ReferenceClock_AddRef(IReferenceClock *iface)
1265 {
1266     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1267
1268     TRACE("(%p/%p)->()\n", This, iface);
1269
1270     return BaseFilterImpl_AddRef((IBaseFilter*)This);
1271 }
1272
1273 static ULONG WINAPI ReferenceClock_Release(IReferenceClock *iface)
1274 {
1275     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1276
1277     TRACE("(%p/%p)->()\n", This, iface);
1278
1279     return DSoundRender_Release((IBaseFilter*)This);
1280 }
1281
1282 /*** IReferenceClock methods ***/
1283 static HRESULT WINAPI ReferenceClock_GetTime(IReferenceClock *iface,
1284                                              REFERENCE_TIME *pTime)
1285 {
1286     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1287     HRESULT hr = E_FAIL;
1288
1289     TRACE("(%p/%p)->(%p)\n", This, iface, pTime);
1290     if (!pTime)
1291         return E_POINTER;
1292
1293     if (This->dsbuffer) {
1294         DWORD writepos1, writepos2;
1295         EnterCriticalSection(&This->filter.csFilter);
1296         DSoundRender_UpdatePositions(This, &writepos1, &writepos2);
1297         *pTime = This->play_time + time_from_pos(This, This->last_playpos);
1298         LeaveCriticalSection(&This->filter.csFilter);
1299         hr = S_OK;
1300     }
1301     if (FAILED(hr))
1302         WARN("Could not get reference time (%x)!\n", hr);
1303
1304     return hr;
1305 }
1306
1307 static HRESULT WINAPI ReferenceClock_AdviseTime(IReferenceClock *iface,
1308                                                 REFERENCE_TIME rtBaseTime,
1309                                                 REFERENCE_TIME rtStreamTime,
1310                                                 HEVENT hEvent,
1311                                                 DWORD_PTR *pdwAdviseCookie)
1312 {
1313     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1314     REFERENCE_TIME when = rtBaseTime + rtStreamTime;
1315     REFERENCE_TIME future;
1316     TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hEvent, pdwAdviseCookie);
1317
1318     if (when <= 0)
1319         return E_INVALIDARG;
1320
1321     if (!pdwAdviseCookie)
1322         return E_POINTER;
1323
1324     EnterCriticalSection(&This->filter.csFilter);
1325     future = when - This->play_time;
1326     if (!This->threadid && This->dsbuffer) {
1327         This->thread_wait = CreateEventW(0, 0, 0, 0);
1328         This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid);
1329         WaitForSingleObject(This->thread_wait, INFINITE);
1330         CloseHandle(This->thread_wait);
1331     }
1332     LeaveCriticalSection(&This->filter.csFilter);
1333     /* If it's in the past or the next millisecond, trigger immediately  */
1334     if (future <= 10000) {
1335         SetEvent((HANDLE)hEvent);
1336         *pdwAdviseCookie = 0;
1337     } else {
1338         struct dsoundrender_timer *t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t));
1339         t->next = NULL;
1340         t->start = when;
1341         t->periodicity = 0;
1342         t->handle = (HANDLE)hEvent;
1343         t->cookie = InterlockedIncrement(&cookie_counter);
1344         PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0);
1345         *pdwAdviseCookie = t->cookie;
1346     }
1347
1348     return S_OK;
1349 }
1350
1351 static HRESULT WINAPI ReferenceClock_AdvisePeriodic(IReferenceClock *iface,
1352                                                     REFERENCE_TIME rtStartTime,
1353                                                     REFERENCE_TIME rtPeriodTime,
1354                                                     HSEMAPHORE hSemaphore,
1355                                                     DWORD_PTR *pdwAdviseCookie)
1356 {
1357     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1358     struct dsoundrender_timer *t;
1359
1360     TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtStartTime), wine_dbgstr_longlong(rtPeriodTime), (void*)hSemaphore, pdwAdviseCookie);
1361
1362     if (rtStartTime <= 0 || rtPeriodTime <= 0)
1363         return E_INVALIDARG;
1364
1365     if (!pdwAdviseCookie)
1366         return E_POINTER;
1367
1368     EnterCriticalSection(&This->filter.csFilter);
1369     if (!This->threadid && This->dsbuffer) {
1370         This->thread_wait = CreateEventW(0, 0, 0, 0);
1371         This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid);
1372         WaitForSingleObject(This->thread_wait, INFINITE);
1373         CloseHandle(This->thread_wait);
1374     }
1375     LeaveCriticalSection(&This->filter.csFilter);
1376
1377     t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t));
1378     t->next = NULL;
1379     t->start = rtStartTime;
1380     t->periodicity = rtPeriodTime;
1381     t->handle = (HANDLE)hSemaphore;
1382     t->cookie = InterlockedIncrement(&cookie_counter);
1383     PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0);
1384     *pdwAdviseCookie = t->cookie;
1385
1386     return S_OK;
1387 }
1388
1389 static HRESULT WINAPI ReferenceClock_Unadvise(IReferenceClock *iface,
1390                                               DWORD_PTR dwAdviseCookie)
1391 {
1392     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1393
1394     TRACE("(%p/%p)->(%p)\n", This, iface, (void*)dwAdviseCookie);
1395     if (!This->advisethread || !dwAdviseCookie)
1396         return S_FALSE;
1397     PostThreadMessageW(This->threadid, WM_APP+3, dwAdviseCookie, 0);
1398     return S_OK;
1399 }
1400
1401 static const IReferenceClockVtbl IReferenceClock_Vtbl =
1402 {
1403     ReferenceClock_QueryInterface,
1404     ReferenceClock_AddRef,
1405     ReferenceClock_Release,
1406     ReferenceClock_GetTime,
1407     ReferenceClock_AdviseTime,
1408     ReferenceClock_AdvisePeriodic,
1409     ReferenceClock_Unadvise
1410 };
1411
1412 /*** IUnknown methods ***/
1413 static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
1414                                                 REFIID riid,
1415                                                 LPVOID*ppvObj)
1416 {
1417     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1418
1419     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1420
1421     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1422 }
1423
1424 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
1425 {
1426     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1427
1428     TRACE("(%p/%p)->()\n", This, iface);
1429
1430     return BaseFilterImpl_AddRef((IBaseFilter*)This);
1431 }
1432
1433 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
1434 {
1435     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1436
1437     TRACE("(%p/%p)->()\n", This, iface);
1438
1439     return DSoundRender_Release((IBaseFilter*)This);
1440 }
1441
1442 /*** IAMDirectSound methods ***/
1443 static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface,  IDirectSound **ds)
1444 {
1445     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1446
1447     FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1448
1449     return E_NOTIMPL;
1450 }
1451
1452 static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1453 {
1454     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1455
1456     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1457
1458     return E_NOTIMPL;
1459 }
1460
1461 static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1462 {
1463     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1464
1465     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1466
1467     return E_NOTIMPL;
1468 }
1469
1470 static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
1471 {
1472     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1473
1474     FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1475
1476     return E_NOTIMPL;
1477 }
1478
1479 static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1480 {
1481     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1482
1483     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1484
1485     return E_NOTIMPL;
1486 }
1487
1488 static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1489 {
1490     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1491
1492     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1493
1494     return E_NOTIMPL;
1495 }
1496
1497 static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgsilent)
1498 {
1499     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1500
1501     FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgsilent);
1502
1503     return E_NOTIMPL;
1504 }
1505
1506 static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND hwnd)
1507 {
1508     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1509
1510     FIXME("(%p/%p)->(%p): stub\n", This, iface, hwnd);
1511
1512     return E_NOTIMPL;
1513 }
1514
1515 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
1516 {
1517     AMDirectSound_QueryInterface,
1518     AMDirectSound_AddRef,
1519     AMDirectSound_Release,
1520     AMDirectSound_GetDirectSoundInterface,
1521     AMDirectSound_GetPrimaryBufferInterface,
1522     AMDirectSound_GetSecondaryBufferInterface,
1523     AMDirectSound_ReleaseDirectSoundInterface,
1524     AMDirectSound_ReleasePrimaryBufferInterface,
1525     AMDirectSound_ReleaseSecondaryBufferInterface,
1526     AMDirectSound_SetFocusWindow,
1527     AMDirectSound_GetFocusWindow
1528 };
1529
1530 static DSoundRenderImpl *from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface) {
1531     return (DSoundRenderImpl*)((char*)iface - offsetof(DSoundRenderImpl, IAMFilterMiscFlags_vtbl));
1532 }
1533
1534 static HRESULT WINAPI AMFilterMiscFlags_QueryInterface(IAMFilterMiscFlags *iface, REFIID riid, void **ppv) {
1535     DSoundRenderImpl *This = from_IAMFilterMiscFlags(iface);
1536     return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
1537 }
1538
1539 static ULONG WINAPI AMFilterMiscFlags_AddRef(IAMFilterMiscFlags *iface) {
1540     DSoundRenderImpl *This = from_IAMFilterMiscFlags(iface);
1541     return IUnknown_AddRef((IUnknown*)This);
1542 }
1543
1544 static ULONG WINAPI AMFilterMiscFlags_Release(IAMFilterMiscFlags *iface) {
1545     DSoundRenderImpl *This = from_IAMFilterMiscFlags(iface);
1546     return IUnknown_Release((IUnknown*)This);
1547 }
1548
1549 static ULONG WINAPI AMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags *iface) {
1550     return AM_FILTER_MISC_FLAGS_IS_RENDERER;
1551 }
1552
1553 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl = {
1554     AMFilterMiscFlags_QueryInterface,
1555     AMFilterMiscFlags_AddRef,
1556     AMFilterMiscFlags_Release,
1557     AMFilterMiscFlags_GetMiscFlags
1558 };