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