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