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