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