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