quartz: Do not try renderers first. Use a single enumeration pass.
[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
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
40
41 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
42
43 static const IBaseFilterVtbl DSoundRender_Vtbl;
44 static const IPinVtbl DSoundRender_InputPin_Vtbl;
45 static const IBasicAudioVtbl IBasicAudio_Vtbl;
46 static const IReferenceClockVtbl IReferenceClock_Vtbl;
47 static const IMediaSeekingVtbl IMediaSeeking_Vtbl;
48
49 typedef struct DSoundRenderImpl
50 {
51     const IBaseFilterVtbl * lpVtbl;
52     const IBasicAudioVtbl *IBasicAudio_vtbl;
53     const IReferenceClockVtbl *IReferenceClock_vtbl;
54
55     LONG refCount;
56     CRITICAL_SECTION csFilter;
57     FILTER_STATE state;
58     REFERENCE_TIME rtStreamStart, rtLastStop;
59     IReferenceClock * pClock;
60     FILTER_INFO filterInfo;
61
62     InputPin * pInputPin;
63
64     IDirectSound8 *dsound;
65     LPDIRECTSOUNDBUFFER dsbuffer;
66     DWORD buf_size;
67     DWORD write_pos;
68     DWORD write_loops;
69
70     DWORD last_play_pos;
71     DWORD play_loops;
72
73     REFERENCE_TIME play_time;
74     MediaSeekingImpl mediaSeeking;
75
76     HANDLE state_change, blocked;
77
78     long volume;
79     long pan;
80 } DSoundRenderImpl;
81
82 /* Seeking is not needed for a renderer, rely on newsegment for the appropriate changes */
83 static HRESULT sound_mod_stop(IBaseFilter *iface)
84 {
85     TRACE("(%p)\n", iface);
86     return S_OK;
87 }
88
89 static HRESULT sound_mod_start(IBaseFilter *iface)
90 {
91     TRACE("(%p)\n", iface);
92
93     return S_OK;
94 }
95
96 static HRESULT sound_mod_rate(IBaseFilter *iface)
97 {
98     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
99
100     WAVEFORMATEX *format = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
101     DWORD freq = format->nSamplesPerSec;
102     double rate = This->mediaSeeking.dRate;
103
104     freq = (DWORD)((double)freq * rate);
105
106     TRACE("(%p)\n", iface);
107
108     if (freq > DSBFREQUENCY_MAX)
109         return VFW_E_UNSUPPORTED_AUDIO;
110
111     if (freq < DSBFREQUENCY_MIN)
112         return VFW_E_UNSUPPORTED_AUDIO;
113
114     return S_OK;
115 }
116
117 static inline HRESULT DSoundRender_GetPos(DSoundRenderImpl *This, DWORD *pPlayPos, REFERENCE_TIME *pRefTime)
118 {
119     HRESULT hr;
120
121     EnterCriticalSection(&This->csFilter);
122     {
123         DWORD state;
124         DWORD write_pos;
125
126         hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
127         if (SUCCEEDED(hr) && !(state & DSBSTATUS_PLAYING) && This->state == State_Running)
128         {
129             TRACE("Not playing, kickstarting the engine\n");
130
131             hr = IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
132             if (FAILED(hr))
133                 ERR("Can't play sound buffer (%x)\n", hr);
134         }
135
136         if (SUCCEEDED(hr))
137             hr = IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, pPlayPos, &write_pos);
138         if (hr == S_OK)
139         {
140             DWORD play_pos = *pPlayPos;
141
142             if (play_pos < This->last_play_pos)
143                 This->play_loops++;
144             This->last_play_pos = play_pos;
145
146             /* If we really fell behind, start at the next possible position
147              * Also happens when just starting playback for the first time,
148              * or when flushing
149              */
150             if ((This->play_loops*This->buf_size)+play_pos >=
151                 (This->write_loops*This->buf_size)+This->write_pos)
152                 This->write_pos = write_pos;
153
154             if (pRefTime)
155             {
156                 REFERENCE_TIME play_time;
157                 play_time = ((REFERENCE_TIME)This->play_loops*10000000) +
158                             ((REFERENCE_TIME)play_pos*10000000/This->buf_size);
159
160                 /* Don't let time run backwards */
161                 if(play_time-This->play_time > 0)
162                     This->play_time = play_time;
163                 else
164                     hr = S_FALSE;
165
166                 *pRefTime = This->play_time;
167             }
168         }
169     }
170     LeaveCriticalSection(&This->csFilter);
171
172     return hr;
173 }
174
175 static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* This, const BYTE *data, DWORD size)
176 {
177     HRESULT hr = S_OK;
178     LPBYTE lpbuf1 = NULL;
179     LPBYTE lpbuf2 = NULL;
180     DWORD dwsize1 = 0;
181     DWORD dwsize2 = 0;
182     DWORD size2;
183     DWORD play_pos,buf_free;
184
185     do {
186
187         hr = DSoundRender_GetPos(This, &play_pos, NULL);
188         if (hr != DS_OK)
189         {
190             ERR("GetPos returned error: %x\n", hr);
191             break;
192         }
193         if (This->write_pos <= play_pos)
194              buf_free = play_pos-This->write_pos;
195         else
196              buf_free = This->buf_size - This->write_pos + play_pos;
197
198         /* Wait for enough of the buffer to empty before filling it */
199         if(buf_free < This->buf_size/4)
200         {
201             Sleep(50);
202             continue;
203         }
204
205         size2 = min(buf_free, size);
206         hr = IDirectSoundBuffer_Lock(This->dsbuffer, This->write_pos, size2, (LPVOID *)&lpbuf1, &dwsize1, (LPVOID *)&lpbuf2, &dwsize2, 0);
207         if (hr != DS_OK) {
208             ERR("Unable to lock sound buffer! (%x)\n", hr);
209             break;
210         }
211         /* TRACE("write_pos=%d, size=%d, sz1=%d, sz2=%d\n", This->write_pos, size2, dwsize1, dwsize2); */
212
213         memcpy(lpbuf1, data, dwsize1);
214         if (dwsize2)
215             memcpy(lpbuf2, data + dwsize1, dwsize2);
216
217         hr = IDirectSoundBuffer_Unlock(This->dsbuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
218         if (hr != DS_OK)
219             ERR("Unable to unlock sound buffer! (%x)\n", hr);
220
221         size -= dwsize1 + dwsize2;
222         data += dwsize1 + dwsize2;
223         This->write_pos += dwsize1 + dwsize2;
224         if (This->write_pos >= This->buf_size)
225         {
226             This->write_pos -= This->buf_size;
227             This->write_loops++;
228         }
229     } while (size && This->state == State_Running);
230
231     return hr;
232 }
233
234 static HRESULT DSoundRender_Sample(LPVOID iface, IMediaSample * pSample)
235 {
236     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
237     LPBYTE pbSrcStream = NULL;
238     long cbSrcStream = 0;
239     REFERENCE_TIME tStart, tStop;
240     HRESULT hr;
241
242     TRACE("%p %p\n", iface, pSample);
243
244     /* Slightly incorrect, Pause completes when a frame is received so we should signal
245      * pause completion here, but for sound playing a single frame doesn't make sense
246      */
247
248     if (This->state == State_Stopped)
249         return VFW_E_WRONG_STATE;
250
251     SetEvent(This->state_change);
252
253     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
254     if (FAILED(hr))
255     {
256         ERR("Cannot get pointer to sample data (%x)\n", hr);
257         return hr;
258     }
259
260     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
261     if (FAILED(hr))
262         ERR("Cannot get sample time (%x)\n", hr);
263
264     if (This->rtLastStop != tStart && (IMediaSample_IsDiscontinuity(pSample) == S_FALSE))
265         WARN("Unexpected discontinuity: Last: %u.%03u, tStart: %u.%03u\n",
266             (DWORD)(This->rtLastStop / 10000000), (DWORD)((This->rtLastStop / 10000)%1000),
267             (DWORD)(tStart / 10000000), (DWORD)((tStart / 10000)%1000));
268     This->rtLastStop = tStop;
269
270     if (IMediaSample_IsPreroll(pSample) == S_OK)
271     {
272         TRACE("Preroll!\n");
273         return S_OK;
274     }
275
276     if (This->state == State_Paused)
277     {
278         LeaveCriticalSection(&This->csFilter);
279         WaitForSingleObject(This->blocked, INFINITE);
280         EnterCriticalSection(&This->csFilter);
281         if (This->state == State_Stopped)
282             return VFW_E_WRONG_STATE;
283
284         if (This->state == State_Paused)
285         {
286             /* Assuming we return because of flushing */
287             return S_OK;
288         }
289     }
290
291     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
292     TRACE("Sample data ptr = %p, size = %ld\n", pbSrcStream, cbSrcStream);
293
294 #if 0 /* For debugging purpose */
295     {
296         int i;
297         for(i = 0; i < cbSrcStream; i++)
298         {
299             if ((i!=0) && !(i%16))
300                 TRACE("\n");
301             TRACE("%02x ", pbSrcStream[i]);
302         }
303         TRACE("\n");
304     }
305 #endif
306
307     return DSoundRender_SendSampleData(This, pbSrcStream, cbSrcStream);
308 }
309
310 static HRESULT DSoundRender_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
311 {
312     WAVEFORMATEX* format;
313
314     if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
315         return S_FALSE;
316
317     format =  (WAVEFORMATEX*)pmt->pbFormat;
318     TRACE("Format = %p\n", format);
319     TRACE("wFormatTag = %x %x\n", format->wFormatTag, WAVE_FORMAT_PCM);
320     TRACE("nChannels = %d\n", format->nChannels);
321     TRACE("nSamplesPerSec = %d\n", format->nAvgBytesPerSec);
322     TRACE("nAvgBytesPerSec = %d\n", format->nAvgBytesPerSec);
323     TRACE("nBlockAlign = %d\n", format->nBlockAlign);
324     TRACE("wBitsPerSample = %d\n", format->wBitsPerSample);
325
326     if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
327         return S_FALSE;
328
329     return S_OK;
330 }
331
332 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv)
333 {
334     HRESULT hr;
335     PIN_INFO piInput;
336     DSoundRenderImpl * pDSoundRender;
337
338     TRACE("(%p, %p)\n", pUnkOuter, ppv);
339
340     *ppv = NULL;
341
342     if (pUnkOuter)
343         return CLASS_E_NOAGGREGATION;
344     
345     pDSoundRender = CoTaskMemAlloc(sizeof(DSoundRenderImpl));
346     if (!pDSoundRender)
347         return E_OUTOFMEMORY;
348     ZeroMemory(pDSoundRender, sizeof(DSoundRenderImpl));
349
350     pDSoundRender->lpVtbl = &DSoundRender_Vtbl;
351     pDSoundRender->IBasicAudio_vtbl = &IBasicAudio_Vtbl;
352     pDSoundRender->IReferenceClock_vtbl = &IReferenceClock_Vtbl;
353     pDSoundRender->refCount = 1;
354     InitializeCriticalSection(&pDSoundRender->csFilter);
355     pDSoundRender->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSoundRenderImpl.csFilter");
356     pDSoundRender->state = State_Stopped;
357
358     /* construct input pin */
359     piInput.dir = PINDIR_INPUT;
360     piInput.pFilter = (IBaseFilter *)pDSoundRender;
361     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
362     hr = InputPin_Construct(&DSoundRender_InputPin_Vtbl, &piInput, DSoundRender_Sample, pDSoundRender, DSoundRender_QueryAccept, NULL, &pDSoundRender->csFilter, NULL, (IPin **)&pDSoundRender->pInputPin);
363
364     if (SUCCEEDED(hr))
365     {
366         hr = DirectSoundCreate8(NULL, &pDSoundRender->dsound, NULL);
367         if (FAILED(hr))
368             ERR("Cannot create Direct Sound object (%x)\n", hr);
369         else
370             IDirectSound_SetCooperativeLevel(pDSoundRender->dsound, GetDesktopWindow(), DSSCL_PRIORITY);
371     }
372
373     if (SUCCEEDED(hr))
374     {
375         MediaSeekingImpl_Init((IBaseFilter*)pDSoundRender, sound_mod_stop, sound_mod_start, sound_mod_rate, &pDSoundRender->mediaSeeking, &pDSoundRender->csFilter);
376         pDSoundRender->mediaSeeking.lpVtbl = &IMediaSeeking_Vtbl;
377
378         pDSoundRender->state_change = CreateEventW(NULL, TRUE, TRUE, NULL);
379         pDSoundRender->blocked = CreateEventW(NULL, FALSE, FALSE, NULL);
380
381         if (!pDSoundRender->state_change || !pDSoundRender->blocked)
382         {
383             IUnknown_Release((IUnknown *)pDSoundRender);
384             return HRESULT_FROM_WIN32(GetLastError());
385         }
386
387         *ppv = (LPVOID)pDSoundRender;
388     }
389     else
390     {
391         if (pDSoundRender->pInputPin)
392             IPin_Release((IPin*)pDSoundRender->pInputPin);
393         pDSoundRender->csFilter.DebugInfo->Spare[0] = 0;
394         DeleteCriticalSection(&pDSoundRender->csFilter);
395         CoTaskMemFree(pDSoundRender);
396     }
397
398     return hr;
399 }
400
401 static HRESULT WINAPI DSoundRender_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
402 {
403     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
404     TRACE("(%p, %p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
405
406     *ppv = NULL;
407
408     if (IsEqualIID(riid, &IID_IUnknown))
409         *ppv = (LPVOID)This;
410     else if (IsEqualIID(riid, &IID_IPersist))
411         *ppv = (LPVOID)This;
412     else if (IsEqualIID(riid, &IID_IMediaFilter))
413         *ppv = (LPVOID)This;
414     else if (IsEqualIID(riid, &IID_IBaseFilter))
415         *ppv = (LPVOID)This;
416     else if (IsEqualIID(riid, &IID_IBasicAudio))
417         *ppv = (LPVOID)&(This->IBasicAudio_vtbl);
418     else if (IsEqualIID(riid, &IID_IReferenceClock))
419         *ppv = (LPVOID)&(This->IReferenceClock_vtbl);
420     else if (IsEqualIID(riid, &IID_IMediaSeeking))
421         *ppv = &This->mediaSeeking.lpVtbl;
422
423     if (*ppv)
424     {
425         IUnknown_AddRef((IUnknown *)(*ppv));
426         return S_OK;
427     }
428
429     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
430         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
431
432     return E_NOINTERFACE;
433 }
434
435 static ULONG WINAPI DSoundRender_AddRef(IBaseFilter * iface)
436 {
437     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
438     ULONG refCount = InterlockedIncrement(&This->refCount);
439
440     TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
441
442     return refCount;
443 }
444
445 static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface)
446 {
447     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
448     ULONG refCount = InterlockedDecrement(&This->refCount);
449
450     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
451
452     if (!refCount)
453     {
454         IPin *pConnectedTo;
455
456         if (This->pClock)
457             IReferenceClock_Release(This->pClock);
458
459         if (This->dsbuffer)
460             IDirectSoundBuffer_Release(This->dsbuffer);
461         This->dsbuffer = NULL;
462         if (This->dsound)
463             IDirectSound_Release(This->dsound);
464         This->dsound = NULL;
465        
466         if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo)))
467         {
468             IPin_Disconnect(pConnectedTo);
469             IPin_Release(pConnectedTo);
470         }
471         IPin_Disconnect((IPin *)This->pInputPin);
472
473         IPin_Release((IPin *)This->pInputPin);
474
475         This->lpVtbl = NULL;
476         This->IBasicAudio_vtbl = NULL;
477         
478         This->csFilter.DebugInfo->Spare[0] = 0;
479         DeleteCriticalSection(&This->csFilter);
480
481         CloseHandle(This->state_change);
482         CloseHandle(This->blocked);
483
484         TRACE("Destroying Audio Renderer\n");
485         CoTaskMemFree(This);
486         
487         return 0;
488     }
489     else
490         return refCount;
491 }
492
493 /** IPersist methods **/
494
495 static HRESULT WINAPI DSoundRender_GetClassID(IBaseFilter * iface, CLSID * pClsid)
496 {
497     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
498     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
499
500     *pClsid = CLSID_DSoundRender;
501
502     return S_OK;
503 }
504
505 /** IMediaFilter methods **/
506
507 static HRESULT WINAPI DSoundRender_Stop(IBaseFilter * iface)
508 {
509     HRESULT hr = S_OK;
510     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
511
512     TRACE("(%p/%p)->()\n", This, iface);
513
514     EnterCriticalSection(&This->csFilter);
515     {
516         DWORD state = 0;
517         if (This->dsbuffer)
518         {
519             hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
520             if (SUCCEEDED(hr))
521             {
522                 if (state & DSBSTATUS_PLAYING)
523                     hr = IDirectSoundBuffer_Stop(This->dsbuffer);
524             }
525         }
526         if (SUCCEEDED(hr))
527             This->state = State_Stopped;
528
529         /* Complete our transition */
530         SetEvent(This->state_change);
531         SetEvent(This->blocked);
532     }
533     LeaveCriticalSection(&This->csFilter);
534     
535     return hr;
536 }
537
538 static HRESULT WINAPI DSoundRender_Pause(IBaseFilter * iface)
539 {
540     HRESULT hr = S_OK;
541     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
542     
543     TRACE("(%p/%p)->()\n", This, iface);
544
545     EnterCriticalSection(&This->csFilter);
546     if (This->state != State_Paused)
547     {
548         DWORD state = 0;
549         if (This->state == State_Stopped)
550         {
551             This->pInputPin->end_of_stream = 0;
552         }
553
554         if (This->dsbuffer)
555         {
556             hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
557             if (SUCCEEDED(hr))
558             {
559                 if (state & DSBSTATUS_PLAYING)
560                     hr = IDirectSoundBuffer_Stop(This->dsbuffer);
561             }
562         }
563         if (SUCCEEDED(hr))
564             This->state = State_Paused;
565
566         ResetEvent(This->blocked);
567         ResetEvent(This->state_change);
568     }
569     LeaveCriticalSection(&This->csFilter);
570
571     return hr;
572 }
573
574 static HRESULT WINAPI DSoundRender_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
575 {
576     HRESULT hr = S_OK;
577     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
578
579     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
580
581     EnterCriticalSection(&This->csFilter);
582     {
583         This->rtStreamStart = tStart;
584         if (This->state == State_Paused)
585         {
586             /* Unblock our thread, state changing from paused to running doesn't need a reset for state change */
587             SetEvent(This->blocked);
588         }
589         else if (This->state == State_Stopped)
590         {
591             ResetEvent(This->state_change);
592             This->pInputPin->end_of_stream = 0;
593         }
594
595         This->state = State_Running;
596     }
597     LeaveCriticalSection(&This->csFilter);
598
599     return hr;
600 }
601
602 static HRESULT WINAPI DSoundRender_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
603 {
604     HRESULT hr;
605     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
606
607     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
608
609     if (WaitForSingleObject(This->state_change, dwMilliSecsTimeout) == WAIT_TIMEOUT)
610         hr = VFW_S_STATE_INTERMEDIATE;
611     else
612         hr = S_OK;
613
614     EnterCriticalSection(&This->csFilter);
615     {
616         *pState = This->state;
617     }
618     LeaveCriticalSection(&This->csFilter);
619
620     return S_OK;
621 }
622
623 static HRESULT WINAPI DSoundRender_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
624 {
625     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
626
627     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
628
629     EnterCriticalSection(&This->csFilter);
630     {
631         if (This->pClock)
632             IReferenceClock_Release(This->pClock);
633         This->pClock = pClock;
634         if (This->pClock)
635             IReferenceClock_AddRef(This->pClock);
636     }
637     LeaveCriticalSection(&This->csFilter);
638
639     return S_OK;
640 }
641
642 static HRESULT WINAPI DSoundRender_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
643 {
644     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
645
646     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
647
648     EnterCriticalSection(&This->csFilter);
649     {
650         *ppClock = This->pClock;
651         if (This->pClock)
652             IReferenceClock_AddRef(This->pClock);
653     }
654     LeaveCriticalSection(&This->csFilter);
655     
656     return S_OK;
657 }
658
659 /** IBaseFilter implementation **/
660
661 static HRESULT DSoundRender_GetPin(IBaseFilter *iface, ULONG pos, IPin **pin, DWORD *lastsynctick)
662 {
663     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
664
665     /* Our pins are static, not changing so setting static tick count is ok */
666     *lastsynctick = 0;
667
668     if (pos >= 1)
669         return S_FALSE;
670
671     *pin = (IPin *)This->pInputPin;
672     IPin_AddRef(*pin);
673     return S_OK;
674 }
675
676 static HRESULT WINAPI DSoundRender_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
677 {
678     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
679
680     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
681
682     return IEnumPinsImpl_Construct(ppEnum, DSoundRender_GetPin, iface);
683 }
684
685 static HRESULT WINAPI DSoundRender_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
686 {
687     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
688
689     TRACE("(%p/%p)->(%s,%p)\n", This, iface, debugstr_w(Id), ppPin);
690     
691     FIXME("DSoundRender::FindPin(...)\n");
692
693     /* FIXME: critical section */
694
695     return E_NOTIMPL;
696 }
697
698 static HRESULT WINAPI DSoundRender_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
699 {
700     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
701
702     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
703
704     strcpyW(pInfo->achName, This->filterInfo.achName);
705     pInfo->pGraph = This->filterInfo.pGraph;
706
707     if (pInfo->pGraph)
708         IFilterGraph_AddRef(pInfo->pGraph);
709     
710     return S_OK;
711 }
712
713 static HRESULT WINAPI DSoundRender_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
714 {
715     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
716
717     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
718
719     EnterCriticalSection(&This->csFilter);
720     {
721         if (pName)
722             strcpyW(This->filterInfo.achName, pName);
723         else
724             *This->filterInfo.achName = '\0';
725         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
726     }
727     LeaveCriticalSection(&This->csFilter);
728
729     return S_OK;
730 }
731
732 static HRESULT WINAPI DSoundRender_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
733 {
734     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
735     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
736     return E_NOTIMPL;
737 }
738
739 static const IBaseFilterVtbl DSoundRender_Vtbl =
740 {
741     DSoundRender_QueryInterface,
742     DSoundRender_AddRef,
743     DSoundRender_Release,
744     DSoundRender_GetClassID,
745     DSoundRender_Stop,
746     DSoundRender_Pause,
747     DSoundRender_Run,
748     DSoundRender_GetState,
749     DSoundRender_SetSyncSource,
750     DSoundRender_GetSyncSource,
751     DSoundRender_EnumPins,
752     DSoundRender_FindPin,
753     DSoundRender_QueryFilterInfo,
754     DSoundRender_JoinFilterGraph,
755     DSoundRender_QueryVendorInfo
756 };
757
758 static HRESULT WINAPI DSoundRender_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
759 {
760     InputPin *This = (InputPin *)iface;
761     PIN_DIRECTION pindirReceive;
762     DSoundRenderImpl *DSImpl;
763     HRESULT hr = S_OK;
764
765     TRACE("(%p)->(%p, %p)\n", This, pReceivePin, pmt);
766     dump_AM_MEDIA_TYPE(pmt);
767
768     EnterCriticalSection(This->pin.pCritSec);
769     {
770         DSImpl = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
771         DSImpl->rtLastStop = -1;
772
773         if (This->pin.pConnectedTo)
774             hr = VFW_E_ALREADY_CONNECTED;
775
776         if (SUCCEEDED(hr) && This->pin.fnQueryAccept(This->pin.pUserData, pmt) != S_OK)
777             hr = VFW_E_TYPE_NOT_ACCEPTED;
778
779         if (SUCCEEDED(hr))
780         {
781             IPin_QueryDirection(pReceivePin, &pindirReceive);
782
783             if (pindirReceive != PINDIR_OUTPUT)
784             {
785                 ERR("Can't connect from non-output pin\n");
786                 hr = VFW_E_INVALID_DIRECTION;
787             }
788         }
789
790         if (SUCCEEDED(hr))
791         {
792             WAVEFORMATEX *format;
793             DSBUFFERDESC buf_desc;
794
795             TRACE("MajorType %s\n", debugstr_guid(&pmt->majortype));
796             TRACE("SubType %s\n", debugstr_guid(&pmt->subtype));
797             TRACE("Format %s\n", debugstr_guid(&pmt->formattype));
798             TRACE("Size %d\n", pmt->cbFormat);
799
800             format = (WAVEFORMATEX*)pmt->pbFormat;
801
802             DSImpl->buf_size = format->nAvgBytesPerSec;
803
804             memset(&buf_desc,0,sizeof(DSBUFFERDESC));
805             buf_desc.dwSize = sizeof(DSBUFFERDESC);
806             buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
807                                DSBCAPS_CTRLFREQUENCY |
808                                DSBCAPS_GETCURRENTPOSITION2;
809             buf_desc.dwBufferBytes = DSImpl->buf_size;
810             buf_desc.lpwfxFormat = format;
811             hr = IDirectSound_CreateSoundBuffer(DSImpl->dsound, &buf_desc, &DSImpl->dsbuffer, NULL);
812             if (FAILED(hr))
813                 ERR("Can't create sound buffer (%x)\n", hr);
814         }
815
816         if (SUCCEEDED(hr))
817         {
818             hr = IDirectSoundBuffer_SetVolume(DSImpl->dsbuffer, DSImpl->volume);
819             if (FAILED(hr))
820                 ERR("Can't set volume to %ld (%x)\n", DSImpl->volume, hr);
821
822             hr = IDirectSoundBuffer_SetPan(DSImpl->dsbuffer, DSImpl->pan);
823             if (FAILED(hr))
824                 ERR("Can't set pan to %ld (%x)\n", DSImpl->pan, hr);
825
826             DSImpl->write_pos = 0;
827             hr = S_OK;
828         }
829
830         if (SUCCEEDED(hr))
831         {
832             CopyMediaType(&This->pin.mtCurrent, pmt);
833             This->pin.pConnectedTo = pReceivePin;
834             IPin_AddRef(pReceivePin);
835         }
836         else if (hr != VFW_E_ALREADY_CONNECTED)
837         {
838             if (DSImpl->dsbuffer)
839                 IDirectSoundBuffer_Release(DSImpl->dsbuffer);
840             DSImpl->dsbuffer = NULL;
841         }
842     }
843     LeaveCriticalSection(This->pin.pCritSec);
844
845     return hr;
846 }
847
848 static HRESULT WINAPI DSoundRender_InputPin_Disconnect(IPin * iface)
849 {
850     IPinImpl *This = (IPinImpl*)iface;
851     DSoundRenderImpl *DSImpl;
852
853     TRACE("(%p)->()\n", iface);
854
855     DSImpl = (DSoundRenderImpl*)This->pinInfo.pFilter;
856     if (DSImpl->dsbuffer)
857         IDirectSoundBuffer_Release(DSImpl->dsbuffer);
858     DSImpl->dsbuffer = NULL;
859
860     return IPinImpl_Disconnect(iface);
861 }
862
863 static HRESULT WINAPI DSoundRender_InputPin_EndOfStream(IPin * iface)
864 {
865     InputPin* This = (InputPin*)iface;
866     DSoundRenderImpl *me = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
867     IMediaEventSink* pEventSink;
868     HRESULT hr;
869
870     EnterCriticalSection(This->pin.pCritSec);
871
872     TRACE("(%p/%p)->()\n", This, iface);
873     InputPin_EndOfStream(iface);
874
875     hr = IFilterGraph_QueryInterface(me->filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
876     if (SUCCEEDED(hr))
877     {
878         BYTE * silence;
879
880         silence = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, me->buf_size);
881         if (silence)
882         {
883             memset(silence, 0, me->buf_size);
884             DSoundRender_SendSampleData((DSoundRenderImpl*)This->pin.pinInfo.pFilter, silence, me->buf_size);
885             HeapFree(GetProcessHeap(), 0, silence);
886         }
887
888         hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, 0);
889         IMediaEventSink_Release(pEventSink);
890     }
891     LeaveCriticalSection(This->pin.pCritSec);
892
893     return hr;
894 }
895
896 static HRESULT WINAPI DSoundRender_InputPin_BeginFlush(IPin * iface)
897 {
898     InputPin *This = (InputPin *)iface;
899     DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
900     HRESULT hr;
901     LPBYTE buffer;
902     DWORD size;
903
904     TRACE("\n");
905
906     EnterCriticalSection(This->pin.pCritSec);
907     hr = InputPin_BeginFlush(iface);
908
909     if (pFilter->dsbuffer)
910     {
911         IDirectSoundBuffer_Stop(pFilter->dsbuffer);
912
913         /* Force a reset */
914         IDirectSoundBuffer_SetCurrentPosition(pFilter->dsbuffer, 0);
915         pFilter->write_pos = pFilter->last_play_pos = 0;
916         ++pFilter->play_loops;
917         pFilter->write_loops = pFilter->play_loops;
918
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     }
923
924     if (pFilter->state == State_Paused)
925         SetEvent(pFilter->blocked);
926     LeaveCriticalSection(This->pin.pCritSec);
927
928     return hr;
929 }
930
931 static HRESULT WINAPI DSoundRender_InputPin_EndFlush(IPin * iface)
932 {
933     InputPin *This = (InputPin *)iface;
934     DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
935     HRESULT hr;
936
937     TRACE("\n");
938
939     EnterCriticalSection(This->pin.pCritSec);
940     hr = InputPin_EndFlush(iface);
941
942     if (pFilter->state == State_Paused)
943         SetEvent(pFilter->blocked);
944     LeaveCriticalSection(This->pin.pCritSec);
945
946     return hr;
947 }
948
949 static const IPinVtbl DSoundRender_InputPin_Vtbl =
950 {
951     InputPin_QueryInterface,
952     IPinImpl_AddRef,
953     InputPin_Release,
954     InputPin_Connect,
955     DSoundRender_InputPin_ReceiveConnection,
956     DSoundRender_InputPin_Disconnect,
957     IPinImpl_ConnectedTo,
958     IPinImpl_ConnectionMediaType,
959     IPinImpl_QueryPinInfo,
960     IPinImpl_QueryDirection,
961     IPinImpl_QueryId,
962     IPinImpl_QueryAccept,
963     IPinImpl_EnumMediaTypes,
964     IPinImpl_QueryInternalConnections,
965     DSoundRender_InputPin_EndOfStream,
966     DSoundRender_InputPin_BeginFlush,
967     DSoundRender_InputPin_EndFlush,
968     InputPin_NewSegment
969 };
970
971 /*** IUnknown methods ***/
972 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
973                                                 REFIID riid,
974                                                 LPVOID*ppvObj) {
975     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
976
977     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
978
979     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
980 }
981
982 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
983     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
984
985     TRACE("(%p/%p)->()\n", This, iface);
986
987     return DSoundRender_AddRef((IBaseFilter*)This);
988 }
989
990 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
991     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
992
993     TRACE("(%p/%p)->()\n", This, iface);
994
995     return DSoundRender_Release((IBaseFilter*)This);
996 }
997
998 /*** IDispatch methods ***/
999 static HRESULT WINAPI Basicaudio_GetTypeInfoCount(IBasicAudio *iface,
1000                                                   UINT*pctinfo) {
1001     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1002
1003     TRACE("(%p/%p)->(%p): stub !!!\n", This, iface, pctinfo);
1004
1005     return S_OK;
1006 }
1007
1008 static HRESULT WINAPI Basicaudio_GetTypeInfo(IBasicAudio *iface,
1009                                              UINT iTInfo,
1010                                              LCID lcid,
1011                                              ITypeInfo**ppTInfo) {
1012     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1013
1014     TRACE("(%p/%p)->(%d, %d, %p): stub !!!\n", This, iface, iTInfo, lcid, ppTInfo);
1015
1016     return S_OK;
1017 }
1018
1019 static HRESULT WINAPI Basicaudio_GetIDsOfNames(IBasicAudio *iface,
1020                                                REFIID riid,
1021                                                LPOLESTR*rgszNames,
1022                                                UINT cNames,
1023                                                LCID lcid,
1024                                                DISPID*rgDispId) {
1025     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1026
1027     TRACE("(%p/%p)->(%s (%p), %p, %d, %d, %p): stub !!!\n", This, iface, debugstr_guid(riid), riid, rgszNames, cNames, lcid, rgDispId);
1028
1029     return S_OK;
1030 }
1031
1032 static HRESULT WINAPI Basicaudio_Invoke(IBasicAudio *iface,
1033                                         DISPID dispIdMember,
1034                                         REFIID riid,
1035                                         LCID lcid,
1036                                         WORD wFlags,
1037                                         DISPPARAMS*pDispParams,
1038                                         VARIANT*pVarResult,
1039                                         EXCEPINFO*pExepInfo,
1040                                         UINT*puArgErr) {
1041     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1042
1043     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);
1044
1045     return S_OK;
1046 }
1047
1048 /*** IBasicAudio methods ***/
1049 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
1050                                             long lVolume) {
1051     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1052
1053     TRACE("(%p/%p)->(%ld)\n", This, iface, lVolume);
1054
1055     if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
1056         return E_INVALIDARG;
1057
1058     if (This->dsbuffer) {
1059         if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
1060             return E_FAIL;
1061     }
1062
1063     This->volume = lVolume;
1064     return S_OK;
1065 }
1066
1067 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
1068                                             long *plVolume) {
1069     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1070
1071     TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
1072
1073     if (!plVolume)
1074         return E_POINTER;
1075
1076     *plVolume = This->volume;
1077     return S_OK;
1078 }
1079
1080 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
1081                                              long lBalance) {
1082     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1083
1084     TRACE("(%p/%p)->(%ld)\n", This, iface, lBalance);
1085
1086     if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
1087         return E_INVALIDARG;
1088
1089     if (This->dsbuffer) {
1090         if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
1091             return E_FAIL;
1092     }
1093
1094     This->pan = lBalance;
1095     return S_OK;
1096 }
1097
1098 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
1099                                              long *plBalance) {
1100     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1101
1102     TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
1103
1104     if (!plBalance)
1105         return E_POINTER;
1106
1107     *plBalance = This->pan;
1108     return S_OK;
1109 }
1110
1111 static const IBasicAudioVtbl IBasicAudio_Vtbl =
1112 {
1113     Basicaudio_QueryInterface,
1114     Basicaudio_AddRef,
1115     Basicaudio_Release,
1116     Basicaudio_GetTypeInfoCount,
1117     Basicaudio_GetTypeInfo,
1118     Basicaudio_GetIDsOfNames,
1119     Basicaudio_Invoke,
1120     Basicaudio_put_Volume,
1121     Basicaudio_get_Volume,
1122     Basicaudio_put_Balance,
1123     Basicaudio_get_Balance
1124 };
1125
1126
1127 /*** IUnknown methods ***/
1128 static HRESULT WINAPI ReferenceClock_QueryInterface(IReferenceClock *iface,
1129                                                 REFIID riid,
1130                                                 LPVOID*ppvObj)
1131 {
1132     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1133
1134     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1135
1136     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1137 }
1138
1139 static ULONG WINAPI ReferenceClock_AddRef(IReferenceClock *iface)
1140 {
1141     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1142
1143     TRACE("(%p/%p)->()\n", This, iface);
1144
1145     return DSoundRender_AddRef((IBaseFilter*)This);
1146 }
1147
1148 static ULONG WINAPI ReferenceClock_Release(IReferenceClock *iface)
1149 {
1150     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1151
1152     TRACE("(%p/%p)->()\n", This, iface);
1153
1154     return DSoundRender_Release((IBaseFilter*)This);
1155 }
1156
1157 /*** IReferenceClock methods ***/
1158 static HRESULT WINAPI ReferenceClock_GetTime(IReferenceClock *iface,
1159                                              REFERENCE_TIME *pTime)
1160 {
1161     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1162     HRESULT hr = E_FAIL;
1163     DWORD play_pos;
1164
1165     TRACE("(%p/%p)->(%p)\n", This, iface, pTime);
1166
1167     if (This->dsbuffer)
1168         hr = DSoundRender_GetPos(This, &play_pos, pTime);
1169     if (FAILED(hr))
1170         ERR("Could not get reference time (%x)!\n", hr);
1171
1172     return hr;
1173 }
1174
1175 static HRESULT WINAPI ReferenceClock_AdviseTime(IReferenceClock *iface,
1176                                                 REFERENCE_TIME rtBaseTime,
1177                                                 REFERENCE_TIME rtStreamTime,
1178                                                 HEVENT hEvent,
1179                                                 DWORD_PTR *pdwAdviseCookie)
1180 {
1181     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1182
1183     FIXME("(%p/%p)->(%s, %s, %p, %p): stub!\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hEvent, pdwAdviseCookie);
1184
1185     return E_NOTIMPL;
1186 }
1187
1188 static HRESULT WINAPI ReferenceClock_AdvisePeriodic(IReferenceClock *iface,
1189                                                     REFERENCE_TIME rtBaseTime,
1190                                                     REFERENCE_TIME rtStreamTime,
1191                                                     HSEMAPHORE hSemaphore,
1192                                                     DWORD_PTR *pdwAdviseCookie)
1193 {
1194     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1195
1196     FIXME("(%p/%p)->(%s, %s, %p, %p): stub!\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hSemaphore, pdwAdviseCookie);
1197
1198     return E_NOTIMPL;
1199 }
1200
1201 static HRESULT WINAPI ReferenceClock_Unadvise(IReferenceClock *iface,
1202                                               DWORD_PTR dwAdviseCookie)
1203 {
1204     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1205
1206     FIXME("(%p/%p)->(%p): stub!\n", This, iface, (void*)dwAdviseCookie);
1207
1208     return S_FALSE;
1209 }
1210
1211 static const IReferenceClockVtbl IReferenceClock_Vtbl =
1212 {
1213     ReferenceClock_QueryInterface,
1214     ReferenceClock_AddRef,
1215     ReferenceClock_Release,
1216     ReferenceClock_GetTime,
1217     ReferenceClock_AdviseTime,
1218     ReferenceClock_AdvisePeriodic,
1219     ReferenceClock_Unadvise
1220 };
1221
1222 static inline DSoundRenderImpl *impl_from_IMediaSeeking( IMediaSeeking *iface )
1223 {
1224     return (DSoundRenderImpl *)((char*)iface - FIELD_OFFSET(DSoundRenderImpl, mediaSeeking.lpVtbl));
1225 }
1226
1227 static HRESULT WINAPI sound_seek_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1228 {
1229     DSoundRenderImpl *This = impl_from_IMediaSeeking(iface);
1230
1231     return IUnknown_QueryInterface((IUnknown *)This, riid, ppv);
1232 }
1233
1234 static ULONG WINAPI sound_seek_AddRef(IMediaSeeking * iface)
1235 {
1236     DSoundRenderImpl *This = impl_from_IMediaSeeking(iface);
1237
1238     return IUnknown_AddRef((IUnknown *)This);
1239 }
1240
1241 static ULONG WINAPI sound_seek_Release(IMediaSeeking * iface)
1242 {
1243     DSoundRenderImpl *This = impl_from_IMediaSeeking(iface);
1244
1245     return IUnknown_Release((IUnknown *)This);
1246 }
1247
1248 static const IMediaSeekingVtbl IMediaSeeking_Vtbl =
1249 {
1250     sound_seek_QueryInterface,
1251     sound_seek_AddRef,
1252     sound_seek_Release,
1253     MediaSeekingImpl_GetCapabilities,
1254     MediaSeekingImpl_CheckCapabilities,
1255     MediaSeekingImpl_IsFormatSupported,
1256     MediaSeekingImpl_QueryPreferredFormat,
1257     MediaSeekingImpl_GetTimeFormat,
1258     MediaSeekingImpl_IsUsingTimeFormat,
1259     MediaSeekingImpl_SetTimeFormat,
1260     MediaSeekingImpl_GetDuration,
1261     MediaSeekingImpl_GetStopPosition,
1262     MediaSeekingImpl_GetCurrentPosition,
1263     MediaSeekingImpl_ConvertTimeFormat,
1264     MediaSeekingImpl_SetPositions,
1265     MediaSeekingImpl_GetPositions,
1266     MediaSeekingImpl_GetAvailable,
1267     MediaSeekingImpl_SetRate,
1268     MediaSeekingImpl_GetRate,
1269     MediaSeekingImpl_GetPreroll
1270 };