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