quartz: Don't return a pin in filesource when there is none.
[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     LPDIRECTSOUND 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 = (WAVEFORMATEX*)pmt->pbFormat;
298     TRACE("wFormatTag = %x %x\n", format->wFormatTag, WAVE_FORMAT_PCM);
299     TRACE("nChannels = %d\n", format->nChannels);
300     TRACE("nSamplesPerSec = %d\n", format->nAvgBytesPerSec);
301     TRACE("nAvgBytesPerSec = %d\n", format->nAvgBytesPerSec);
302     TRACE("nBlockAlign = %d\n", format->nBlockAlign);
303     TRACE("wBitsPerSample = %d\n", format->wBitsPerSample);
304
305     if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio) && IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
306         return S_OK;
307     return S_FALSE;
308 }
309
310 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv)
311 {
312     HRESULT hr;
313     PIN_INFO piInput;
314     DSoundRenderImpl * pDSoundRender;
315
316     TRACE("(%p, %p)\n", pUnkOuter, ppv);
317
318     *ppv = NULL;
319
320     if (pUnkOuter)
321         return CLASS_E_NOAGGREGATION;
322     
323     pDSoundRender = CoTaskMemAlloc(sizeof(DSoundRenderImpl));
324     if (!pDSoundRender)
325         return E_OUTOFMEMORY;
326     ZeroMemory(pDSoundRender, sizeof(DSoundRenderImpl));
327
328     pDSoundRender->lpVtbl = &DSoundRender_Vtbl;
329     pDSoundRender->IBasicAudio_vtbl = &IBasicAudio_Vtbl;
330     pDSoundRender->IReferenceClock_vtbl = &IReferenceClock_Vtbl;
331     pDSoundRender->refCount = 1;
332     InitializeCriticalSection(&pDSoundRender->csFilter);
333     pDSoundRender->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSoundRenderImpl.csFilter");
334     pDSoundRender->state = State_Stopped;
335
336     /* construct input pin */
337     piInput.dir = PINDIR_INPUT;
338     piInput.pFilter = (IBaseFilter *)pDSoundRender;
339     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
340     hr = InputPin_Construct(&DSoundRender_InputPin_Vtbl, &piInput, DSoundRender_Sample, pDSoundRender, DSoundRender_QueryAccept, NULL, &pDSoundRender->csFilter, (IPin **)&pDSoundRender->pInputPin);
341
342     if (SUCCEEDED(hr))
343     {
344         hr = DirectSoundCreate(NULL, &pDSoundRender->dsound, NULL);
345         if (FAILED(hr))
346             ERR("Cannot create Direct Sound object (%x)\n", hr);
347     }
348
349     if (SUCCEEDED(hr))
350     {
351         MediaSeekingImpl_Init((IBaseFilter*)pDSoundRender, sound_mod_stop, sound_mod_start, sound_mod_rate, &pDSoundRender->mediaSeeking, &pDSoundRender->csFilter);
352         pDSoundRender->mediaSeeking.lpVtbl = &IMediaSeeking_Vtbl;
353
354         *ppv = (LPVOID)pDSoundRender;
355     }
356     else
357     {
358         if (pDSoundRender->pInputPin)
359             IPin_Release((IPin*)pDSoundRender->pInputPin);
360         pDSoundRender->csFilter.DebugInfo->Spare[0] = 0;
361         DeleteCriticalSection(&pDSoundRender->csFilter);
362         CoTaskMemFree(pDSoundRender);
363     }
364
365     return hr;
366 }
367
368 static HRESULT WINAPI DSoundRender_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
369 {
370     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
371     TRACE("(%p, %p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
372
373     *ppv = NULL;
374
375     if (IsEqualIID(riid, &IID_IUnknown))
376         *ppv = (LPVOID)This;
377     else if (IsEqualIID(riid, &IID_IPersist))
378         *ppv = (LPVOID)This;
379     else if (IsEqualIID(riid, &IID_IMediaFilter))
380         *ppv = (LPVOID)This;
381     else if (IsEqualIID(riid, &IID_IBaseFilter))
382         *ppv = (LPVOID)This;
383     else if (IsEqualIID(riid, &IID_IBasicAudio))
384         *ppv = (LPVOID)&(This->IBasicAudio_vtbl);
385     else if (IsEqualIID(riid, &IID_IReferenceClock))
386         *ppv = (LPVOID)&(This->IReferenceClock_vtbl);
387     else if (IsEqualIID(riid, &IID_IMediaSeeking))
388         *ppv = &This->mediaSeeking.lpVtbl;
389
390     if (*ppv)
391     {
392         IUnknown_AddRef((IUnknown *)(*ppv));
393         return S_OK;
394     }
395
396     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
397         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
398
399     return E_NOINTERFACE;
400 }
401
402 static ULONG WINAPI DSoundRender_AddRef(IBaseFilter * iface)
403 {
404     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
405     ULONG refCount = InterlockedIncrement(&This->refCount);
406
407     TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
408
409     return refCount;
410 }
411
412 static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface)
413 {
414     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
415     ULONG refCount = InterlockedDecrement(&This->refCount);
416
417     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
418
419     if (!refCount)
420     {
421         IPin *pConnectedTo;
422
423         if (This->pClock)
424             IReferenceClock_Release(This->pClock);
425
426         if (This->dsbuffer)
427             IDirectSoundBuffer_Release(This->dsbuffer);
428         This->dsbuffer = NULL;
429         if (This->dsound)
430             IDirectSound_Release(This->dsound);
431         This->dsound = NULL;
432        
433         if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo)))
434         {
435             IPin_Disconnect(pConnectedTo);
436             IPin_Release(pConnectedTo);
437         }
438         IPin_Disconnect((IPin *)This->pInputPin);
439
440         IPin_Release((IPin *)This->pInputPin);
441
442         This->lpVtbl = NULL;
443         This->IBasicAudio_vtbl = NULL;
444         
445         This->csFilter.DebugInfo->Spare[0] = 0;
446         DeleteCriticalSection(&This->csFilter);
447
448         TRACE("Destroying Audio Renderer\n");
449         CoTaskMemFree(This);
450         
451         return 0;
452     }
453     else
454         return refCount;
455 }
456
457 /** IPersist methods **/
458
459 static HRESULT WINAPI DSoundRender_GetClassID(IBaseFilter * iface, CLSID * pClsid)
460 {
461     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
462     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
463
464     *pClsid = CLSID_DSoundRender;
465
466     return S_OK;
467 }
468
469 /** IMediaFilter methods **/
470
471 static HRESULT WINAPI DSoundRender_Stop(IBaseFilter * iface)
472 {
473     HRESULT hr = S_OK;
474     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
475
476     TRACE("(%p/%p)->()\n", This, iface);
477
478     EnterCriticalSection(&This->csFilter);
479     {
480         DWORD state = 0;
481         if (This->dsbuffer)
482         {
483             hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
484             if (SUCCEEDED(hr))
485             {
486                 if (state & DSBSTATUS_PLAYING)
487                     hr = IDirectSoundBuffer_Stop(This->dsbuffer);
488             }
489         }
490         if (SUCCEEDED(hr))
491             This->state = State_Stopped;
492     }
493     LeaveCriticalSection(&This->csFilter);
494     
495     return hr;
496 }
497
498 static HRESULT WINAPI DSoundRender_Pause(IBaseFilter * iface)
499 {
500     HRESULT hr = S_OK;
501     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
502     
503     TRACE("(%p/%p)->()\n", This, iface);
504
505     EnterCriticalSection(&This->csFilter);
506     {
507         DWORD state = 0;
508         if (This->state == State_Stopped)
509             This->pInputPin->end_of_stream = 0;
510
511         if (This->dsbuffer)
512         {
513             hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
514             if (SUCCEEDED(hr))
515             {
516                 if (state & DSBSTATUS_PLAYING)
517                     hr = IDirectSoundBuffer_Stop(This->dsbuffer);
518             }
519         }
520         if (SUCCEEDED(hr))
521             This->state = State_Paused;
522     }
523     LeaveCriticalSection(&This->csFilter);
524
525     return hr;
526 }
527
528 static HRESULT WINAPI DSoundRender_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
529 {
530     HRESULT hr = S_OK;
531     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
532
533     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
534
535     EnterCriticalSection(&This->csFilter);
536     {
537         This->rtStreamStart = tStart;
538         This->state = State_Running;
539         This->pInputPin->end_of_stream = 0;
540     }
541     LeaveCriticalSection(&This->csFilter);
542
543     return hr;
544 }
545
546 static HRESULT WINAPI DSoundRender_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
547 {
548     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
549
550     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
551
552     EnterCriticalSection(&This->csFilter);
553     {
554         *pState = This->state;
555     }
556     LeaveCriticalSection(&This->csFilter);
557
558     return S_OK;
559 }
560
561 static HRESULT WINAPI DSoundRender_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
562 {
563     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
564
565     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
566
567     EnterCriticalSection(&This->csFilter);
568     {
569         if (This->pClock)
570             IReferenceClock_Release(This->pClock);
571         This->pClock = pClock;
572         if (This->pClock)
573             IReferenceClock_AddRef(This->pClock);
574     }
575     LeaveCriticalSection(&This->csFilter);
576
577     return S_OK;
578 }
579
580 static HRESULT WINAPI DSoundRender_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
581 {
582     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
583
584     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
585
586     EnterCriticalSection(&This->csFilter);
587     {
588         *ppClock = This->pClock;
589         if (This->pClock)
590             IReferenceClock_AddRef(This->pClock);
591     }
592     LeaveCriticalSection(&This->csFilter);
593     
594     return S_OK;
595 }
596
597 /** IBaseFilter implementation **/
598
599 static HRESULT DSoundRender_GetPin(IBaseFilter *iface, ULONG pos, IPin **pin, DWORD *lastsynctick)
600 {
601     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
602
603     /* Our pins are static, not changing so setting static tick count is ok */
604     *lastsynctick = 0;
605
606     if (pos >= 1)
607         return S_FALSE;
608
609     *pin = (IPin *)This->pInputPin;
610     IPin_AddRef(*pin);
611     return S_OK;
612 }
613
614 static HRESULT WINAPI DSoundRender_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
615 {
616     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
617
618     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
619
620     return IEnumPinsImpl_Construct(ppEnum, DSoundRender_GetPin, iface);
621 }
622
623 static HRESULT WINAPI DSoundRender_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
624 {
625     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
626
627     TRACE("(%p/%p)->(%s,%p)\n", This, iface, debugstr_w(Id), ppPin);
628     
629     FIXME("DSoundRender::FindPin(...)\n");
630
631     /* FIXME: critical section */
632
633     return E_NOTIMPL;
634 }
635
636 static HRESULT WINAPI DSoundRender_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
637 {
638     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
639
640     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
641
642     strcpyW(pInfo->achName, This->filterInfo.achName);
643     pInfo->pGraph = This->filterInfo.pGraph;
644
645     if (pInfo->pGraph)
646         IFilterGraph_AddRef(pInfo->pGraph);
647     
648     return S_OK;
649 }
650
651 static HRESULT WINAPI DSoundRender_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
652 {
653     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
654
655     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
656
657     EnterCriticalSection(&This->csFilter);
658     {
659         if (pName)
660             strcpyW(This->filterInfo.achName, pName);
661         else
662             *This->filterInfo.achName = '\0';
663         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
664     }
665     LeaveCriticalSection(&This->csFilter);
666
667     return S_OK;
668 }
669
670 static HRESULT WINAPI DSoundRender_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
671 {
672     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
673     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
674     return E_NOTIMPL;
675 }
676
677 static const IBaseFilterVtbl DSoundRender_Vtbl =
678 {
679     DSoundRender_QueryInterface,
680     DSoundRender_AddRef,
681     DSoundRender_Release,
682     DSoundRender_GetClassID,
683     DSoundRender_Stop,
684     DSoundRender_Pause,
685     DSoundRender_Run,
686     DSoundRender_GetState,
687     DSoundRender_SetSyncSource,
688     DSoundRender_GetSyncSource,
689     DSoundRender_EnumPins,
690     DSoundRender_FindPin,
691     DSoundRender_QueryFilterInfo,
692     DSoundRender_JoinFilterGraph,
693     DSoundRender_QueryVendorInfo
694 };
695
696 static HRESULT WINAPI DSoundRender_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
697 {
698     InputPin *This = (InputPin *)iface;
699     PIN_DIRECTION pindirReceive;
700     DSoundRenderImpl *DSImpl;
701     HRESULT hr = S_OK;
702
703     TRACE("(%p)->(%p, %p)\n", This, pReceivePin, pmt);
704     dump_AM_MEDIA_TYPE(pmt);
705
706     EnterCriticalSection(This->pin.pCritSec);
707     {
708         DSImpl = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
709         DSImpl->rtLastStop = -1;
710
711         if (This->pin.pConnectedTo)
712             hr = VFW_E_ALREADY_CONNECTED;
713
714         if (SUCCEEDED(hr) && This->pin.fnQueryAccept(This->pin.pUserData, pmt) != S_OK)
715             hr = VFW_E_TYPE_NOT_ACCEPTED;
716
717         if (SUCCEEDED(hr))
718         {
719             IPin_QueryDirection(pReceivePin, &pindirReceive);
720
721             if (pindirReceive != PINDIR_OUTPUT)
722             {
723                 ERR("Can't connect from non-output pin\n");
724                 hr = VFW_E_INVALID_DIRECTION;
725             }
726         }
727
728         if (SUCCEEDED(hr))
729         {
730             WAVEFORMATEX *format;
731             DSBUFFERDESC buf_desc;
732
733             TRACE("MajorType %s\n", debugstr_guid(&pmt->majortype));
734             TRACE("SubType %s\n", debugstr_guid(&pmt->subtype));
735             TRACE("Format %s\n", debugstr_guid(&pmt->formattype));
736             TRACE("Size %d\n", pmt->cbFormat);
737
738             format = (WAVEFORMATEX*)pmt->pbFormat;
739
740             DSImpl->buf_size = format->nAvgBytesPerSec;
741
742             memset(&buf_desc,0,sizeof(DSBUFFERDESC));
743             buf_desc.dwSize = sizeof(DSBUFFERDESC);
744             buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
745                                DSBCAPS_CTRLFREQUENCY |
746                                DSBCAPS_GETCURRENTPOSITION2;
747             buf_desc.dwBufferBytes = DSImpl->buf_size;
748             buf_desc.lpwfxFormat = format;
749             hr = IDirectSound_CreateSoundBuffer(DSImpl->dsound, &buf_desc, &DSImpl->dsbuffer, NULL);
750             if (FAILED(hr))
751                 ERR("Can't create sound buffer (%x)\n", hr);
752         }
753
754         if (SUCCEEDED(hr))
755         {
756             hr = IDirectSoundBuffer_SetVolume(DSImpl->dsbuffer, DSImpl->volume);
757             if (FAILED(hr))
758                 ERR("Can't set volume to %ld (%x)\n", DSImpl->volume, hr);
759
760             hr = IDirectSoundBuffer_SetPan(DSImpl->dsbuffer, DSImpl->pan);
761             if (FAILED(hr))
762                 ERR("Can't set pan to %ld (%x)\n", DSImpl->pan, hr);
763
764             DSImpl->write_pos = 0;
765             hr = S_OK;
766         }
767
768         if (SUCCEEDED(hr))
769         {
770             CopyMediaType(&This->pin.mtCurrent, pmt);
771             This->pin.pConnectedTo = pReceivePin;
772             IPin_AddRef(pReceivePin);
773         }
774         else
775         {
776             if (DSImpl->dsbuffer)
777                 IDirectSoundBuffer_Release(DSImpl->dsbuffer);
778             DSImpl->dsbuffer = NULL;
779         }
780     }
781     LeaveCriticalSection(This->pin.pCritSec);
782
783     return hr;
784 }
785
786 static HRESULT WINAPI DSoundRender_InputPin_Disconnect(IPin * iface)
787 {
788     IPinImpl *This = (IPinImpl*)iface;
789     DSoundRenderImpl *DSImpl;
790
791     TRACE("(%p)->()\n", iface);
792
793     DSImpl = (DSoundRenderImpl*)This->pinInfo.pFilter;
794     if (DSImpl->dsbuffer)
795         IDirectSoundBuffer_Release(DSImpl->dsbuffer);
796     DSImpl->dsbuffer = NULL;
797
798     return IPinImpl_Disconnect(iface);
799 }
800
801 static HRESULT WINAPI DSoundRender_InputPin_EndOfStream(IPin * iface)
802 {
803     InputPin* This = (InputPin*)iface;
804     DSoundRenderImpl *me = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
805     IMediaEventSink* pEventSink;
806     HRESULT hr;
807
808     EnterCriticalSection(This->pin.pCritSec);
809
810     TRACE("(%p/%p)->()\n", This, iface);
811     InputPin_EndOfStream(iface);
812
813     hr = IFilterGraph_QueryInterface(me->filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
814     if (SUCCEEDED(hr))
815     {
816         BYTE * silence;
817
818         silence = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, me->buf_size);
819         if (silence)
820         {
821             memset(silence, 0, me->buf_size);
822             DSoundRender_SendSampleData((DSoundRenderImpl*)This->pin.pinInfo.pFilter, silence, me->buf_size);
823             HeapFree(GetProcessHeap(), 0, silence);
824         }
825
826         hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, 0);
827         IMediaEventSink_Release(pEventSink);
828     }
829     LeaveCriticalSection(This->pin.pCritSec);
830
831     return hr;
832 }
833
834 static HRESULT WINAPI DSoundRender_InputPin_BeginFlush(IPin * iface)
835 {
836     InputPin *This = (InputPin *)iface;
837     DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
838     HRESULT hr;
839     LPBYTE buffer;
840     DWORD size;
841
842     TRACE("\n");
843
844     EnterCriticalSection(This->pin.pCritSec);
845     hr = InputPin_BeginFlush(iface);
846
847     if (pFilter->dsbuffer)
848     {
849         IDirectSoundBuffer_Stop(pFilter->dsbuffer);
850
851         /* Force a reset */
852         IDirectSoundBuffer_SetCurrentPosition(pFilter->dsbuffer, 0);
853         pFilter->write_pos = pFilter->last_play_pos = 0;
854         ++pFilter->play_loops;
855         pFilter->write_loops = pFilter->play_loops;
856
857         IDirectSoundBuffer_Lock(pFilter->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
858         memset(buffer, 0, size);
859         IDirectSoundBuffer_Unlock(pFilter->dsbuffer, buffer, size, NULL, 0);
860     }
861     LeaveCriticalSection(This->pin.pCritSec);
862
863     return hr;
864 }
865
866 static const IPinVtbl DSoundRender_InputPin_Vtbl =
867 {
868     InputPin_QueryInterface,
869     IPinImpl_AddRef,
870     InputPin_Release,
871     InputPin_Connect,
872     DSoundRender_InputPin_ReceiveConnection,
873     DSoundRender_InputPin_Disconnect,
874     IPinImpl_ConnectedTo,
875     IPinImpl_ConnectionMediaType,
876     IPinImpl_QueryPinInfo,
877     IPinImpl_QueryDirection,
878     IPinImpl_QueryId,
879     IPinImpl_QueryAccept,
880     IPinImpl_EnumMediaTypes,
881     IPinImpl_QueryInternalConnections,
882     DSoundRender_InputPin_EndOfStream,
883     DSoundRender_InputPin_BeginFlush,
884     InputPin_EndFlush,
885     InputPin_NewSegment
886 };
887
888 static const IMemInputPinVtbl MemInputPin_Vtbl = 
889 {
890     MemInputPin_QueryInterface,
891     MemInputPin_AddRef,
892     MemInputPin_Release,
893     MemInputPin_GetAllocator,
894     MemInputPin_NotifyAllocator,
895     MemInputPin_GetAllocatorRequirements,
896     MemInputPin_Receive,
897     MemInputPin_ReceiveMultiple,
898     MemInputPin_ReceiveCanBlock
899 };
900
901 /*** IUnknown methods ***/
902 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
903                                                 REFIID riid,
904                                                 LPVOID*ppvObj) {
905     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
906
907     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
908
909     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
910 }
911
912 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
913     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
914
915     TRACE("(%p/%p)->()\n", This, iface);
916
917     return DSoundRender_AddRef((IBaseFilter*)This);
918 }
919
920 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
921     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
922
923     TRACE("(%p/%p)->()\n", This, iface);
924
925     return DSoundRender_Release((IBaseFilter*)This);
926 }
927
928 /*** IDispatch methods ***/
929 static HRESULT WINAPI Basicaudio_GetTypeInfoCount(IBasicAudio *iface,
930                                                   UINT*pctinfo) {
931     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
932
933     TRACE("(%p/%p)->(%p): stub !!!\n", This, iface, pctinfo);
934
935     return S_OK;
936 }
937
938 static HRESULT WINAPI Basicaudio_GetTypeInfo(IBasicAudio *iface,
939                                              UINT iTInfo,
940                                              LCID lcid,
941                                              ITypeInfo**ppTInfo) {
942     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
943
944     TRACE("(%p/%p)->(%d, %d, %p): stub !!!\n", This, iface, iTInfo, lcid, ppTInfo);
945
946     return S_OK;
947 }
948
949 static HRESULT WINAPI Basicaudio_GetIDsOfNames(IBasicAudio *iface,
950                                                REFIID riid,
951                                                LPOLESTR*rgszNames,
952                                                UINT cNames,
953                                                LCID lcid,
954                                                DISPID*rgDispId) {
955     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
956
957     TRACE("(%p/%p)->(%s (%p), %p, %d, %d, %p): stub !!!\n", This, iface, debugstr_guid(riid), riid, rgszNames, cNames, lcid, rgDispId);
958
959     return S_OK;
960 }
961
962 static HRESULT WINAPI Basicaudio_Invoke(IBasicAudio *iface,
963                                         DISPID dispIdMember,
964                                         REFIID riid,
965                                         LCID lcid,
966                                         WORD wFlags,
967                                         DISPPARAMS*pDispParams,
968                                         VARIANT*pVarResult,
969                                         EXCEPINFO*pExepInfo,
970                                         UINT*puArgErr) {
971     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
972
973     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);
974
975     return S_OK;
976 }
977
978 /*** IBasicAudio methods ***/
979 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
980                                             long lVolume) {
981     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
982
983     TRACE("(%p/%p)->(%ld)\n", This, iface, lVolume);
984
985     if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
986         return E_INVALIDARG;
987
988     if (This->dsbuffer) {
989         if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
990             return E_FAIL;
991     }
992
993     This->volume = lVolume;
994     return S_OK;
995 }
996
997 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
998                                             long *plVolume) {
999     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1000
1001     TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
1002
1003     if (!plVolume)
1004         return E_POINTER;
1005
1006     *plVolume = This->volume;
1007     return S_OK;
1008 }
1009
1010 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
1011                                              long lBalance) {
1012     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1013
1014     TRACE("(%p/%p)->(%ld)\n", This, iface, lBalance);
1015
1016     if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
1017         return E_INVALIDARG;
1018
1019     if (This->dsbuffer) {
1020         if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
1021             return E_FAIL;
1022     }
1023
1024     This->pan = lBalance;
1025     return S_OK;
1026 }
1027
1028 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
1029                                              long *plBalance) {
1030     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1031
1032     TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
1033
1034     if (!plBalance)
1035         return E_POINTER;
1036
1037     *plBalance = This->pan;
1038     return S_OK;
1039 }
1040
1041 static const IBasicAudioVtbl IBasicAudio_Vtbl =
1042 {
1043     Basicaudio_QueryInterface,
1044     Basicaudio_AddRef,
1045     Basicaudio_Release,
1046     Basicaudio_GetTypeInfoCount,
1047     Basicaudio_GetTypeInfo,
1048     Basicaudio_GetIDsOfNames,
1049     Basicaudio_Invoke,
1050     Basicaudio_put_Volume,
1051     Basicaudio_get_Volume,
1052     Basicaudio_put_Balance,
1053     Basicaudio_get_Balance
1054 };
1055
1056
1057 /*** IUnknown methods ***/
1058 static HRESULT WINAPI ReferenceClock_QueryInterface(IReferenceClock *iface,
1059                                                 REFIID riid,
1060                                                 LPVOID*ppvObj)
1061 {
1062     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1063
1064     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1065
1066     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1067 }
1068
1069 static ULONG WINAPI ReferenceClock_AddRef(IReferenceClock *iface)
1070 {
1071     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1072
1073     TRACE("(%p/%p)->()\n", This, iface);
1074
1075     return DSoundRender_AddRef((IBaseFilter*)This);
1076 }
1077
1078 static ULONG WINAPI ReferenceClock_Release(IReferenceClock *iface)
1079 {
1080     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1081
1082     TRACE("(%p/%p)->()\n", This, iface);
1083
1084     return DSoundRender_Release((IBaseFilter*)This);
1085 }
1086
1087 /*** IReferenceClock methods ***/
1088 static HRESULT WINAPI ReferenceClock_GetTime(IReferenceClock *iface,
1089                                              REFERENCE_TIME *pTime)
1090 {
1091     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1092     HRESULT hr = E_FAIL;
1093     DWORD play_pos;
1094
1095     TRACE("(%p/%p)->(%p)\n", This, iface, pTime);
1096
1097     if (This->dsbuffer)
1098         hr = DSoundRender_GetPos(This, &play_pos, pTime);
1099     if (FAILED(hr))
1100         ERR("Could not get reference time (%x)!\n", hr);
1101
1102     return hr;
1103 }
1104
1105 static HRESULT WINAPI ReferenceClock_AdviseTime(IReferenceClock *iface,
1106                                                 REFERENCE_TIME rtBaseTime,
1107                                                 REFERENCE_TIME rtStreamTime,
1108                                                 HEVENT hEvent,
1109                                                 DWORD_PTR *pdwAdviseCookie)
1110 {
1111     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1112
1113     FIXME("(%p/%p)->(%s, %s, %p, %p): stub!\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hEvent, pdwAdviseCookie);
1114
1115     return E_NOTIMPL;
1116 }
1117
1118 static HRESULT WINAPI ReferenceClock_AdvisePeriodic(IReferenceClock *iface,
1119                                                     REFERENCE_TIME rtBaseTime,
1120                                                     REFERENCE_TIME rtStreamTime,
1121                                                     HSEMAPHORE hSemaphore,
1122                                                     DWORD_PTR *pdwAdviseCookie)
1123 {
1124     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1125
1126     FIXME("(%p/%p)->(%s, %s, %p, %p): stub!\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hSemaphore, pdwAdviseCookie);
1127
1128     return E_NOTIMPL;
1129 }
1130
1131 static HRESULT WINAPI ReferenceClock_Unadvise(IReferenceClock *iface,
1132                                               DWORD_PTR dwAdviseCookie)
1133 {
1134     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1135
1136     FIXME("(%p/%p)->(%p): stub!\n", This, iface, (void*)dwAdviseCookie);
1137
1138     return S_FALSE;
1139 }
1140
1141 static const IReferenceClockVtbl IReferenceClock_Vtbl =
1142 {
1143     ReferenceClock_QueryInterface,
1144     ReferenceClock_AddRef,
1145     ReferenceClock_Release,
1146     ReferenceClock_GetTime,
1147     ReferenceClock_AdviseTime,
1148     ReferenceClock_AdvisePeriodic,
1149     ReferenceClock_Unadvise
1150 };
1151
1152 static inline DSoundRenderImpl *impl_from_IMediaSeeking( IMediaSeeking *iface )
1153 {
1154     return (DSoundRenderImpl *)((char*)iface - FIELD_OFFSET(DSoundRenderImpl, mediaSeeking.lpVtbl));
1155 }
1156
1157 static HRESULT WINAPI sound_seek_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1158 {
1159     DSoundRenderImpl *This = impl_from_IMediaSeeking(iface);
1160
1161     return IUnknown_QueryInterface((IUnknown *)This, riid, ppv);
1162 }
1163
1164 static ULONG WINAPI sound_seek_AddRef(IMediaSeeking * iface)
1165 {
1166     DSoundRenderImpl *This = impl_from_IMediaSeeking(iface);
1167
1168     return IUnknown_AddRef((IUnknown *)This);
1169 }
1170
1171 static ULONG WINAPI sound_seek_Release(IMediaSeeking * iface)
1172 {
1173     DSoundRenderImpl *This = impl_from_IMediaSeeking(iface);
1174
1175     return IUnknown_Release((IUnknown *)This);
1176 }
1177
1178 static const IMediaSeekingVtbl IMediaSeeking_Vtbl =
1179 {
1180     sound_seek_QueryInterface,
1181     sound_seek_AddRef,
1182     sound_seek_Release,
1183     MediaSeekingImpl_GetCapabilities,
1184     MediaSeekingImpl_CheckCapabilities,
1185     MediaSeekingImpl_IsFormatSupported,
1186     MediaSeekingImpl_QueryPreferredFormat,
1187     MediaSeekingImpl_GetTimeFormat,
1188     MediaSeekingImpl_IsUsingTimeFormat,
1189     MediaSeekingImpl_SetTimeFormat,
1190     MediaSeekingImpl_GetDuration,
1191     MediaSeekingImpl_GetStopPosition,
1192     MediaSeekingImpl_GetCurrentPosition,
1193     MediaSeekingImpl_ConvertTimeFormat,
1194     MediaSeekingImpl_SetPositions,
1195     MediaSeekingImpl_GetPositions,
1196     MediaSeekingImpl_GetAvailable,
1197     MediaSeekingImpl_SetRate,
1198     MediaSeekingImpl_GetRate,
1199     MediaSeekingImpl_GetPreroll
1200 };