2 * Direct Sound Audio Renderer
4 * Copyright 2004 Christian Costa
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.
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.
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
23 #include "quartz_private.h"
24 #include "control_private.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
42 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
44 static const IBaseFilterVtbl DSoundRender_Vtbl;
45 static const IPinVtbl DSoundRender_InputPin_Vtbl;
46 static const IBasicAudioVtbl IBasicAudio_Vtbl;
47 static const IReferenceClockVtbl IReferenceClock_Vtbl;
48 static const IMediaSeekingVtbl IMediaSeeking_Vtbl;
49 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl;
51 typedef struct DSoundRenderImpl
53 const IBaseFilterVtbl * lpVtbl;
54 const IBasicAudioVtbl *IBasicAudio_vtbl;
55 const IReferenceClockVtbl *IReferenceClock_vtbl;
56 const IAMDirectSoundVtbl *IAMDirectSound_vtbl;
59 CRITICAL_SECTION csFilter;
61 REFERENCE_TIME rtStreamStart, rtLastStop;
62 IReferenceClock * pClock;
63 FILTER_INFO filterInfo;
67 IDirectSound8 *dsound;
68 LPDIRECTSOUNDBUFFER dsbuffer;
77 REFERENCE_TIME play_time;
78 MediaSeekingImpl mediaSeeking;
80 HANDLE state_change, blocked;
86 /* Seeking is not needed for a renderer, rely on newsegment for the appropriate changes */
87 static HRESULT sound_mod_stop(IBaseFilter *iface)
89 TRACE("(%p)\n", iface);
93 static HRESULT sound_mod_start(IBaseFilter *iface)
95 TRACE("(%p)\n", iface);
100 static HRESULT sound_mod_rate(IBaseFilter *iface)
102 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
104 WAVEFORMATEX *format = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
105 DWORD freq = format->nSamplesPerSec;
106 double rate = This->mediaSeeking.dRate;
108 freq = (DWORD)((double)freq * rate);
110 TRACE("(%p)\n", iface);
112 if (freq > DSBFREQUENCY_MAX)
113 return VFW_E_UNSUPPORTED_AUDIO;
115 if (freq < DSBFREQUENCY_MIN)
116 return VFW_E_UNSUPPORTED_AUDIO;
121 static inline HRESULT DSoundRender_GetPos(DSoundRenderImpl *This, DWORD *pPlayPos, REFERENCE_TIME *pRefTime)
125 EnterCriticalSection(&This->csFilter);
130 hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
131 if (SUCCEEDED(hr) && !(state & DSBSTATUS_PLAYING) && This->state == State_Running)
133 TRACE("Not playing, kickstarting the engine\n");
135 hr = IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
137 ERR("Can't play sound buffer (%x)\n", hr);
141 hr = IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, pPlayPos, &write_pos);
144 DWORD play_pos = *pPlayPos;
146 if (play_pos < This->last_play_pos)
148 This->last_play_pos = play_pos;
150 /* If we really fell behind, start at the next possible position
151 * Also happens when just starting playback for the first time,
154 if ((This->play_loops*This->buf_size)+play_pos >=
155 (This->write_loops*This->buf_size)+This->write_pos)
156 This->write_pos = write_pos;
160 REFERENCE_TIME play_time;
161 play_time = ((REFERENCE_TIME)This->play_loops*10000000) +
162 ((REFERENCE_TIME)play_pos*10000000/This->buf_size);
164 /* Don't let time run backwards */
165 if(play_time-This->play_time > 0)
166 This->play_time = play_time;
170 *pRefTime = This->play_time;
174 LeaveCriticalSection(&This->csFilter);
179 static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* This, const BYTE *data, DWORD size)
182 LPBYTE lpbuf1 = NULL;
183 LPBYTE lpbuf2 = NULL;
187 DWORD play_pos,buf_free;
191 hr = DSoundRender_GetPos(This, &play_pos, NULL);
194 ERR("GetPos returned error: %x\n", hr);
197 if (This->write_pos <= play_pos)
198 buf_free = play_pos-This->write_pos;
200 buf_free = This->buf_size - This->write_pos + play_pos;
202 /* Wait for enough of the buffer to empty before filling it */
203 if(buf_free < This->buf_size/20)
207 LeaveCriticalSection(&This->csFilter);
208 ret = WaitForSingleObject(This->blocked, 50);
209 if (ret != WAIT_TIMEOUT)
211 EnterCriticalSection(&This->csFilter);
213 if (This->pInputPin->flushing)
214 return VFW_E_WRONG_STATE;
215 if (This->state == State_Stopped)
216 return VFW_E_WRONG_STATE;
220 size2 = min(buf_free, size);
221 hr = IDirectSoundBuffer_Lock(This->dsbuffer, This->write_pos, size2, (LPVOID *)&lpbuf1, &dwsize1, (LPVOID *)&lpbuf2, &dwsize2, 0);
223 ERR("Unable to lock sound buffer! (%x)\n", hr);
226 /* TRACE("write_pos=%d, size=%d, sz1=%d, sz2=%d\n", This->write_pos, size2, dwsize1, dwsize2); */
228 memcpy(lpbuf1, data, dwsize1);
230 memcpy(lpbuf2, data + dwsize1, dwsize2);
232 hr = IDirectSoundBuffer_Unlock(This->dsbuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
234 ERR("Unable to unlock sound buffer! (%x)\n", hr);
236 size -= dwsize1 + dwsize2;
237 data += dwsize1 + dwsize2;
238 This->write_pos += dwsize1 + dwsize2;
239 if (This->write_pos >= This->buf_size)
241 This->write_pos -= This->buf_size;
244 } while (size && This->state == State_Running);
249 static HRESULT DSoundRender_Sample(LPVOID iface, IMediaSample * pSample)
251 DSoundRenderImpl *This = iface;
252 LPBYTE pbSrcStream = NULL;
253 LONG cbSrcStream = 0;
254 REFERENCE_TIME tStart, tStop;
258 TRACE("%p %p\n", iface, pSample);
260 /* Slightly incorrect, Pause completes when a frame is received so we should signal
261 * pause completion here, but for sound playing a single frame doesn't make sense
264 EnterCriticalSection(&This->csFilter);
266 if (This->pInputPin->end_of_stream || This->pInputPin->flushing)
268 LeaveCriticalSection(&This->csFilter);
272 if (This->state == State_Stopped)
274 LeaveCriticalSection(&This->csFilter);
275 return VFW_E_WRONG_STATE;
278 if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
280 AM_MEDIA_TYPE *orig = &This->pInputPin->pin.mtCurrent;
281 WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
282 WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
284 if (origfmt->wFormatTag == newfmt->wFormatTag &&
285 origfmt->nChannels == newfmt->nChannels &&
286 origfmt->nBlockAlign == newfmt->nBlockAlign &&
287 origfmt->wBitsPerSample == newfmt->wBitsPerSample &&
288 origfmt->cbSize == newfmt->cbSize)
290 if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec)
292 hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer,
293 newfmt->nSamplesPerSec);
296 LeaveCriticalSection(&This->csFilter);
297 return VFW_E_TYPE_NOT_ACCEPTED;
300 CopyMediaType(orig, amt);
301 IMediaSample_SetMediaType(pSample, NULL);
306 LeaveCriticalSection(&This->csFilter);
307 return VFW_E_TYPE_NOT_ACCEPTED;
311 hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
314 ERR("Cannot get pointer to sample data (%x)\n", hr);
315 LeaveCriticalSection(&This->csFilter);
319 hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
321 ERR("Cannot get sample time (%x)\n", hr);
323 if (This->rtLastStop != tStart && (IMediaSample_IsDiscontinuity(pSample) == S_FALSE))
324 WARN("Unexpected discontinuity: Last: %u.%03u, tStart: %u.%03u\n",
325 (DWORD)(This->rtLastStop / 10000000), (DWORD)((This->rtLastStop / 10000)%1000),
326 (DWORD)(tStart / 10000000), (DWORD)((tStart / 10000)%1000));
327 This->rtLastStop = tStop;
329 if (IMediaSample_IsPreroll(pSample) == S_OK)
332 LeaveCriticalSection(&This->csFilter);
336 if (This->state == State_Paused)
338 SetEvent(This->state_change);
339 LeaveCriticalSection(&This->csFilter);
340 WaitForSingleObject(This->blocked, INFINITE);
341 EnterCriticalSection(&This->csFilter);
342 if (This->state == State_Stopped)
344 LeaveCriticalSection(&This->csFilter);
345 return VFW_E_WRONG_STATE;
348 if (This->state == State_Paused)
350 /* Assuming we return because of flushing */
352 LeaveCriticalSection(&This->csFilter);
357 cbSrcStream = IMediaSample_GetActualDataLength(pSample);
358 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
360 #if 0 /* For debugging purpose */
363 for(i = 0; i < cbSrcStream; i++)
365 if ((i!=0) && !(i%16))
367 TRACE("%02x ", pbSrcStream[i]);
373 hr = DSoundRender_SendSampleData(This, pbSrcStream, cbSrcStream);
374 SetEvent(This->state_change);
375 LeaveCriticalSection(&This->csFilter);
379 static HRESULT DSoundRender_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
381 WAVEFORMATEX* format;
383 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
386 format = (WAVEFORMATEX*)pmt->pbFormat;
387 TRACE("Format = %p\n", format);
388 TRACE("wFormatTag = %x %x\n", format->wFormatTag, WAVE_FORMAT_PCM);
389 TRACE("nChannels = %d\n", format->nChannels);
390 TRACE("nSamplesPerSec = %d\n", format->nAvgBytesPerSec);
391 TRACE("nAvgBytesPerSec = %d\n", format->nAvgBytesPerSec);
392 TRACE("nBlockAlign = %d\n", format->nBlockAlign);
393 TRACE("wBitsPerSample = %d\n", format->wBitsPerSample);
395 if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
401 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv)
405 DSoundRenderImpl * pDSoundRender;
407 TRACE("(%p, %p)\n", pUnkOuter, ppv);
412 return CLASS_E_NOAGGREGATION;
414 pDSoundRender = CoTaskMemAlloc(sizeof(DSoundRenderImpl));
416 return E_OUTOFMEMORY;
417 ZeroMemory(pDSoundRender, sizeof(DSoundRenderImpl));
419 pDSoundRender->lpVtbl = &DSoundRender_Vtbl;
420 pDSoundRender->IBasicAudio_vtbl = &IBasicAudio_Vtbl;
421 pDSoundRender->IReferenceClock_vtbl = &IReferenceClock_Vtbl;
422 pDSoundRender->IAMDirectSound_vtbl = &IAMDirectSound_Vtbl;
423 pDSoundRender->refCount = 1;
424 InitializeCriticalSection(&pDSoundRender->csFilter);
425 pDSoundRender->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSoundRenderImpl.csFilter");
426 pDSoundRender->state = State_Stopped;
428 /* construct input pin */
429 piInput.dir = PINDIR_INPUT;
430 piInput.pFilter = (IBaseFilter *)pDSoundRender;
431 lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
432 hr = InputPin_Construct(&DSoundRender_InputPin_Vtbl, &piInput, DSoundRender_Sample, pDSoundRender, DSoundRender_QueryAccept, NULL, &pDSoundRender->csFilter, NULL, (IPin **)&pDSoundRender->pInputPin);
436 hr = DirectSoundCreate8(NULL, &pDSoundRender->dsound, NULL);
438 ERR("Cannot create Direct Sound object (%x)\n", hr);
440 IDirectSound_SetCooperativeLevel(pDSoundRender->dsound, GetDesktopWindow(), DSSCL_PRIORITY);
445 MediaSeekingImpl_Init((IBaseFilter*)pDSoundRender, sound_mod_stop, sound_mod_start, sound_mod_rate, &pDSoundRender->mediaSeeking, &pDSoundRender->csFilter);
446 pDSoundRender->mediaSeeking.lpVtbl = &IMediaSeeking_Vtbl;
448 pDSoundRender->state_change = CreateEventW(NULL, TRUE, TRUE, NULL);
449 pDSoundRender->blocked = CreateEventW(NULL, FALSE, FALSE, NULL);
451 if (!pDSoundRender->state_change || !pDSoundRender->blocked)
453 IUnknown_Release((IUnknown *)pDSoundRender);
454 return HRESULT_FROM_WIN32(GetLastError());
457 *ppv = pDSoundRender;
461 if (pDSoundRender->pInputPin)
462 IPin_Release((IPin*)pDSoundRender->pInputPin);
463 pDSoundRender->csFilter.DebugInfo->Spare[0] = 0;
464 DeleteCriticalSection(&pDSoundRender->csFilter);
465 CoTaskMemFree(pDSoundRender);
471 static HRESULT WINAPI DSoundRender_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
473 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
474 TRACE("(%p, %p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
478 if (IsEqualIID(riid, &IID_IUnknown))
480 else if (IsEqualIID(riid, &IID_IPersist))
482 else if (IsEqualIID(riid, &IID_IMediaFilter))
484 else if (IsEqualIID(riid, &IID_IBaseFilter))
486 else if (IsEqualIID(riid, &IID_IBasicAudio))
487 *ppv = &This->IBasicAudio_vtbl;
488 else if (IsEqualIID(riid, &IID_IReferenceClock))
489 *ppv = &This->IReferenceClock_vtbl;
490 else if (IsEqualIID(riid, &IID_IMediaSeeking))
491 *ppv = &This->mediaSeeking.lpVtbl;
492 else if (IsEqualIID(riid, &IID_IAMDirectSound))
493 *ppv = &This->IAMDirectSound_vtbl;
497 IUnknown_AddRef((IUnknown *)(*ppv));
501 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
502 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
504 return E_NOINTERFACE;
507 static ULONG WINAPI DSoundRender_AddRef(IBaseFilter * iface)
509 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
510 ULONG refCount = InterlockedIncrement(&This->refCount);
512 TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
517 static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface)
519 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
520 ULONG refCount = InterlockedDecrement(&This->refCount);
522 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
529 IReferenceClock_Release(This->pClock);
532 IDirectSoundBuffer_Release(This->dsbuffer);
533 This->dsbuffer = NULL;
535 IDirectSound_Release(This->dsound);
538 if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo)))
540 IPin_Disconnect(pConnectedTo);
541 IPin_Release(pConnectedTo);
543 IPin_Disconnect((IPin *)This->pInputPin);
545 IPin_Release((IPin *)This->pInputPin);
548 This->IBasicAudio_vtbl = NULL;
550 This->csFilter.DebugInfo->Spare[0] = 0;
551 DeleteCriticalSection(&This->csFilter);
553 CloseHandle(This->state_change);
554 CloseHandle(This->blocked);
556 TRACE("Destroying Audio Renderer\n");
565 /** IPersist methods **/
567 static HRESULT WINAPI DSoundRender_GetClassID(IBaseFilter * iface, CLSID * pClsid)
569 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
570 TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
572 *pClsid = CLSID_DSoundRender;
577 /** IMediaFilter methods **/
579 static HRESULT WINAPI DSoundRender_Stop(IBaseFilter * iface)
582 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
584 TRACE("(%p/%p)->()\n", This, iface);
586 EnterCriticalSection(&This->csFilter);
591 hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
594 if (state & DSBSTATUS_PLAYING)
595 hr = IDirectSoundBuffer_Stop(This->dsbuffer);
599 This->state = State_Stopped;
601 /* Complete our transition */
602 SetEvent(This->state_change);
603 SetEvent(This->blocked);
605 LeaveCriticalSection(&This->csFilter);
610 static HRESULT WINAPI DSoundRender_Pause(IBaseFilter * iface)
613 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
615 TRACE("(%p/%p)->()\n", This, iface);
617 EnterCriticalSection(&This->csFilter);
618 if (This->state != State_Paused)
621 if (This->state == State_Stopped)
623 This->pInputPin->end_of_stream = 0;
628 hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
631 if (state & DSBSTATUS_PLAYING)
632 hr = IDirectSoundBuffer_Stop(This->dsbuffer);
636 This->state = State_Paused;
638 ResetEvent(This->blocked);
639 ResetEvent(This->state_change);
641 LeaveCriticalSection(&This->csFilter);
646 static HRESULT WINAPI DSoundRender_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
649 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
651 TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
653 EnterCriticalSection(&This->csFilter);
655 This->rtStreamStart = tStart;
656 if (This->state == State_Paused)
658 /* Unblock our thread, state changing from paused to running doesn't need a reset for state change */
659 SetEvent(This->blocked);
661 else if (This->state == State_Stopped)
663 ResetEvent(This->state_change);
664 This->pInputPin->end_of_stream = 0;
666 ResetEvent(This->blocked);
668 This->state = State_Running;
670 LeaveCriticalSection(&This->csFilter);
675 static HRESULT WINAPI DSoundRender_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
678 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
680 TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
682 if (WaitForSingleObject(This->state_change, dwMilliSecsTimeout) == WAIT_TIMEOUT)
683 hr = VFW_S_STATE_INTERMEDIATE;
687 EnterCriticalSection(&This->csFilter);
689 *pState = This->state;
691 LeaveCriticalSection(&This->csFilter);
696 static HRESULT WINAPI DSoundRender_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
698 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
700 TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
702 EnterCriticalSection(&This->csFilter);
705 IReferenceClock_Release(This->pClock);
706 This->pClock = pClock;
708 IReferenceClock_AddRef(This->pClock);
710 LeaveCriticalSection(&This->csFilter);
715 static HRESULT WINAPI DSoundRender_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
717 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
719 TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
721 EnterCriticalSection(&This->csFilter);
723 *ppClock = This->pClock;
725 IReferenceClock_AddRef(This->pClock);
727 LeaveCriticalSection(&This->csFilter);
732 /** IBaseFilter implementation **/
734 static HRESULT DSoundRender_GetPin(IBaseFilter *iface, ULONG pos, IPin **pin, DWORD *lastsynctick)
736 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
738 /* Our pins are static, not changing so setting static tick count is ok */
744 *pin = (IPin *)This->pInputPin;
749 static HRESULT WINAPI DSoundRender_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
751 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
753 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
755 return IEnumPinsImpl_Construct(ppEnum, DSoundRender_GetPin, iface);
758 static HRESULT WINAPI DSoundRender_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
760 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
762 TRACE("(%p/%p)->(%s,%p)\n", This, iface, debugstr_w(Id), ppPin);
764 FIXME("DSoundRender::FindPin(...)\n");
766 /* FIXME: critical section */
771 static HRESULT WINAPI DSoundRender_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
773 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
775 TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
777 strcpyW(pInfo->achName, This->filterInfo.achName);
778 pInfo->pGraph = This->filterInfo.pGraph;
781 IFilterGraph_AddRef(pInfo->pGraph);
786 static HRESULT WINAPI DSoundRender_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
788 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
790 TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
792 EnterCriticalSection(&This->csFilter);
795 strcpyW(This->filterInfo.achName, pName);
797 *This->filterInfo.achName = '\0';
798 This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
800 LeaveCriticalSection(&This->csFilter);
805 static HRESULT WINAPI DSoundRender_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
807 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
808 TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
812 static const IBaseFilterVtbl DSoundRender_Vtbl =
814 DSoundRender_QueryInterface,
816 DSoundRender_Release,
817 DSoundRender_GetClassID,
821 DSoundRender_GetState,
822 DSoundRender_SetSyncSource,
823 DSoundRender_GetSyncSource,
824 DSoundRender_EnumPins,
825 DSoundRender_FindPin,
826 DSoundRender_QueryFilterInfo,
827 DSoundRender_JoinFilterGraph,
828 DSoundRender_QueryVendorInfo
831 static HRESULT WINAPI DSoundRender_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
833 InputPin *This = (InputPin *)iface;
834 PIN_DIRECTION pindirReceive;
835 DSoundRenderImpl *DSImpl;
838 TRACE("(%p)->(%p, %p)\n", This, pReceivePin, pmt);
839 dump_AM_MEDIA_TYPE(pmt);
841 EnterCriticalSection(This->pin.pCritSec);
843 DSImpl = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
844 DSImpl->rtLastStop = -1;
846 if (This->pin.pConnectedTo)
847 hr = VFW_E_ALREADY_CONNECTED;
849 if (SUCCEEDED(hr) && This->pin.fnQueryAccept(This->pin.pUserData, pmt) != S_OK)
850 hr = VFW_E_TYPE_NOT_ACCEPTED;
854 IPin_QueryDirection(pReceivePin, &pindirReceive);
856 if (pindirReceive != PINDIR_OUTPUT)
858 ERR("Can't connect from non-output pin\n");
859 hr = VFW_E_INVALID_DIRECTION;
865 WAVEFORMATEX *format;
866 DSBUFFERDESC buf_desc;
868 TRACE("MajorType %s\n", debugstr_guid(&pmt->majortype));
869 TRACE("SubType %s\n", debugstr_guid(&pmt->subtype));
870 TRACE("Format %s\n", debugstr_guid(&pmt->formattype));
871 TRACE("Size %d\n", pmt->cbFormat);
873 format = (WAVEFORMATEX*)pmt->pbFormat;
875 DSImpl->buf_size = format->nAvgBytesPerSec;
877 memset(&buf_desc,0,sizeof(DSBUFFERDESC));
878 buf_desc.dwSize = sizeof(DSBUFFERDESC);
879 buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
880 DSBCAPS_CTRLFREQUENCY |
881 DSBCAPS_GETCURRENTPOSITION2;
882 buf_desc.dwBufferBytes = DSImpl->buf_size;
883 buf_desc.lpwfxFormat = format;
884 hr = IDirectSound_CreateSoundBuffer(DSImpl->dsound, &buf_desc, &DSImpl->dsbuffer, NULL);
886 ERR("Can't create sound buffer (%x)\n", hr);
891 hr = IDirectSoundBuffer_SetVolume(DSImpl->dsbuffer, DSImpl->volume);
893 ERR("Can't set volume to %d (%x)\n", DSImpl->volume, hr);
895 hr = IDirectSoundBuffer_SetPan(DSImpl->dsbuffer, DSImpl->pan);
897 ERR("Can't set pan to %d (%x)\n", DSImpl->pan, hr);
899 DSImpl->write_pos = 0;
905 CopyMediaType(&This->pin.mtCurrent, pmt);
906 This->pin.pConnectedTo = pReceivePin;
907 IPin_AddRef(pReceivePin);
909 else if (hr != VFW_E_ALREADY_CONNECTED)
911 if (DSImpl->dsbuffer)
912 IDirectSoundBuffer_Release(DSImpl->dsbuffer);
913 DSImpl->dsbuffer = NULL;
916 LeaveCriticalSection(This->pin.pCritSec);
921 static HRESULT WINAPI DSoundRender_InputPin_Disconnect(IPin * iface)
923 IPinImpl *This = (IPinImpl*)iface;
924 DSoundRenderImpl *DSImpl;
926 TRACE("(%p)->()\n", iface);
928 DSImpl = (DSoundRenderImpl*)This->pinInfo.pFilter;
929 if (DSImpl->dsbuffer)
930 IDirectSoundBuffer_Release(DSImpl->dsbuffer);
931 DSImpl->dsbuffer = NULL;
933 return IPinImpl_Disconnect(iface);
936 static HRESULT WINAPI DSoundRender_InputPin_EndOfStream(IPin * iface)
938 InputPin* This = (InputPin*)iface;
939 DSoundRenderImpl *me = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
940 IMediaEventSink* pEventSink;
943 EnterCriticalSection(This->pin.pCritSec);
945 TRACE("(%p/%p)->()\n", This, iface);
946 hr = InputPin_EndOfStream(iface);
950 LeaveCriticalSection(This->pin.pCritSec);
954 hr = IFilterGraph_QueryInterface(me->filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
959 silence = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, me->buf_size);
962 memset(silence, 0, me->buf_size);
963 DSoundRender_SendSampleData((DSoundRenderImpl*)This->pin.pinInfo.pFilter, silence, me->buf_size);
964 HeapFree(GetProcessHeap(), 0, silence);
967 hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, 0);
968 IMediaEventSink_Release(pEventSink);
970 LeaveCriticalSection(This->pin.pCritSec);
975 static HRESULT WINAPI DSoundRender_InputPin_BeginFlush(IPin * iface)
977 InputPin *This = (InputPin *)iface;
978 DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
983 EnterCriticalSection(This->pin.pCritSec);
984 hr = InputPin_BeginFlush(iface);
985 SetEvent(pFilter->blocked);
986 LeaveCriticalSection(This->pin.pCritSec);
991 static HRESULT WINAPI DSoundRender_InputPin_EndFlush(IPin * iface)
993 InputPin *This = (InputPin *)iface;
994 DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
999 EnterCriticalSection(This->pin.pCritSec);
1000 if (pFilter->in_loop) {
1001 ResetEvent(pFilter->state_change);
1002 LeaveCriticalSection(This->pin.pCritSec);
1003 WaitForSingleObject(pFilter->state_change, -1);
1004 EnterCriticalSection(This->pin.pCritSec);
1006 if (pFilter->state != State_Stopped)
1007 ResetEvent(pFilter->blocked);
1009 if (pFilter->dsbuffer)
1013 IDirectSoundBuffer_Stop(pFilter->dsbuffer);
1016 IDirectSoundBuffer_SetCurrentPosition(pFilter->dsbuffer, 0);
1017 pFilter->write_pos = pFilter->last_play_pos = 0;
1018 ++pFilter->play_loops;
1019 pFilter->write_loops = pFilter->play_loops;
1021 IDirectSoundBuffer_Lock(pFilter->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
1022 memset(buffer, 0, size);
1023 IDirectSoundBuffer_Unlock(pFilter->dsbuffer, buffer, size, NULL, 0);
1025 hr = InputPin_EndFlush(iface);
1026 LeaveCriticalSection(This->pin.pCritSec);
1031 static const IPinVtbl DSoundRender_InputPin_Vtbl =
1033 InputPin_QueryInterface,
1037 DSoundRender_InputPin_ReceiveConnection,
1038 DSoundRender_InputPin_Disconnect,
1039 IPinImpl_ConnectedTo,
1040 IPinImpl_ConnectionMediaType,
1041 IPinImpl_QueryPinInfo,
1042 IPinImpl_QueryDirection,
1044 IPinImpl_QueryAccept,
1045 IPinImpl_EnumMediaTypes,
1046 IPinImpl_QueryInternalConnections,
1047 DSoundRender_InputPin_EndOfStream,
1048 DSoundRender_InputPin_BeginFlush,
1049 DSoundRender_InputPin_EndFlush,
1053 /*** IUnknown methods ***/
1054 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
1057 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1059 TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1061 return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1064 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
1065 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1067 TRACE("(%p/%p)->()\n", This, iface);
1069 return DSoundRender_AddRef((IBaseFilter*)This);
1072 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
1073 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1075 TRACE("(%p/%p)->()\n", This, iface);
1077 return DSoundRender_Release((IBaseFilter*)This);
1080 /*** IDispatch methods ***/
1081 static HRESULT WINAPI Basicaudio_GetTypeInfoCount(IBasicAudio *iface,
1083 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1085 TRACE("(%p/%p)->(%p): stub !!!\n", This, iface, pctinfo);
1090 static HRESULT WINAPI Basicaudio_GetTypeInfo(IBasicAudio *iface,
1093 ITypeInfo**ppTInfo) {
1094 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1096 TRACE("(%p/%p)->(%d, %d, %p): stub !!!\n", This, iface, iTInfo, lcid, ppTInfo);
1101 static HRESULT WINAPI Basicaudio_GetIDsOfNames(IBasicAudio *iface,
1107 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1109 TRACE("(%p/%p)->(%s (%p), %p, %d, %d, %p): stub !!!\n", This, iface, debugstr_guid(riid), riid, rgszNames, cNames, lcid, rgDispId);
1114 static HRESULT WINAPI Basicaudio_Invoke(IBasicAudio *iface,
1115 DISPID dispIdMember,
1119 DISPPARAMS*pDispParams,
1121 EXCEPINFO*pExepInfo,
1123 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1125 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);
1130 /*** IBasicAudio methods ***/
1131 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
1133 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1135 TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
1137 if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
1138 return E_INVALIDARG;
1140 if (This->dsbuffer) {
1141 if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
1145 This->volume = lVolume;
1149 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
1151 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1153 TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
1158 *plVolume = This->volume;
1162 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
1164 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1166 TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
1168 if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
1169 return E_INVALIDARG;
1171 if (This->dsbuffer) {
1172 if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
1176 This->pan = lBalance;
1180 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
1182 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1184 TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
1189 *plBalance = This->pan;
1193 static const IBasicAudioVtbl IBasicAudio_Vtbl =
1195 Basicaudio_QueryInterface,
1198 Basicaudio_GetTypeInfoCount,
1199 Basicaudio_GetTypeInfo,
1200 Basicaudio_GetIDsOfNames,
1202 Basicaudio_put_Volume,
1203 Basicaudio_get_Volume,
1204 Basicaudio_put_Balance,
1205 Basicaudio_get_Balance
1209 /*** IUnknown methods ***/
1210 static HRESULT WINAPI ReferenceClock_QueryInterface(IReferenceClock *iface,
1214 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1216 TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1218 return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1221 static ULONG WINAPI ReferenceClock_AddRef(IReferenceClock *iface)
1223 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1225 TRACE("(%p/%p)->()\n", This, iface);
1227 return DSoundRender_AddRef((IBaseFilter*)This);
1230 static ULONG WINAPI ReferenceClock_Release(IReferenceClock *iface)
1232 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1234 TRACE("(%p/%p)->()\n", This, iface);
1236 return DSoundRender_Release((IBaseFilter*)This);
1239 /*** IReferenceClock methods ***/
1240 static HRESULT WINAPI ReferenceClock_GetTime(IReferenceClock *iface,
1241 REFERENCE_TIME *pTime)
1243 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1244 HRESULT hr = E_FAIL;
1247 TRACE("(%p/%p)->(%p)\n", This, iface, pTime);
1250 hr = DSoundRender_GetPos(This, &play_pos, pTime);
1252 ERR("Could not get reference time (%x)!\n", hr);
1257 static HRESULT WINAPI ReferenceClock_AdviseTime(IReferenceClock *iface,
1258 REFERENCE_TIME rtBaseTime,
1259 REFERENCE_TIME rtStreamTime,
1261 DWORD_PTR *pdwAdviseCookie)
1263 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1265 FIXME("(%p/%p)->(%s, %s, %p, %p): stub!\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hEvent, pdwAdviseCookie);
1270 static HRESULT WINAPI ReferenceClock_AdvisePeriodic(IReferenceClock *iface,
1271 REFERENCE_TIME rtBaseTime,
1272 REFERENCE_TIME rtStreamTime,
1273 HSEMAPHORE hSemaphore,
1274 DWORD_PTR *pdwAdviseCookie)
1276 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1278 FIXME("(%p/%p)->(%s, %s, %p, %p): stub!\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hSemaphore, pdwAdviseCookie);
1283 static HRESULT WINAPI ReferenceClock_Unadvise(IReferenceClock *iface,
1284 DWORD_PTR dwAdviseCookie)
1286 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1288 FIXME("(%p/%p)->(%p): stub!\n", This, iface, (void*)dwAdviseCookie);
1293 static const IReferenceClockVtbl IReferenceClock_Vtbl =
1295 ReferenceClock_QueryInterface,
1296 ReferenceClock_AddRef,
1297 ReferenceClock_Release,
1298 ReferenceClock_GetTime,
1299 ReferenceClock_AdviseTime,
1300 ReferenceClock_AdvisePeriodic,
1301 ReferenceClock_Unadvise
1304 static inline DSoundRenderImpl *impl_from_IMediaSeeking( IMediaSeeking *iface )
1306 return (DSoundRenderImpl *)((char*)iface - FIELD_OFFSET(DSoundRenderImpl, mediaSeeking.lpVtbl));
1309 static HRESULT WINAPI sound_seek_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1311 DSoundRenderImpl *This = impl_from_IMediaSeeking(iface);
1313 return IUnknown_QueryInterface((IUnknown *)This, riid, ppv);
1316 static ULONG WINAPI sound_seek_AddRef(IMediaSeeking * iface)
1318 DSoundRenderImpl *This = impl_from_IMediaSeeking(iface);
1320 return IUnknown_AddRef((IUnknown *)This);
1323 static ULONG WINAPI sound_seek_Release(IMediaSeeking * iface)
1325 DSoundRenderImpl *This = impl_from_IMediaSeeking(iface);
1327 return IUnknown_Release((IUnknown *)This);
1330 static const IMediaSeekingVtbl IMediaSeeking_Vtbl =
1332 sound_seek_QueryInterface,
1335 MediaSeekingImpl_GetCapabilities,
1336 MediaSeekingImpl_CheckCapabilities,
1337 MediaSeekingImpl_IsFormatSupported,
1338 MediaSeekingImpl_QueryPreferredFormat,
1339 MediaSeekingImpl_GetTimeFormat,
1340 MediaSeekingImpl_IsUsingTimeFormat,
1341 MediaSeekingImpl_SetTimeFormat,
1342 MediaSeekingImpl_GetDuration,
1343 MediaSeekingImpl_GetStopPosition,
1344 MediaSeekingImpl_GetCurrentPosition,
1345 MediaSeekingImpl_ConvertTimeFormat,
1346 MediaSeekingImpl_SetPositions,
1347 MediaSeekingImpl_GetPositions,
1348 MediaSeekingImpl_GetAvailable,
1349 MediaSeekingImpl_SetRate,
1350 MediaSeekingImpl_GetRate,
1351 MediaSeekingImpl_GetPreroll
1354 /*** IUnknown methods ***/
1355 static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
1359 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1361 TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1363 return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1366 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
1368 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1370 TRACE("(%p/%p)->()\n", This, iface);
1372 return DSoundRender_AddRef((IBaseFilter*)This);
1375 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
1377 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1379 TRACE("(%p/%p)->()\n", This, iface);
1381 return DSoundRender_Release((IBaseFilter*)This);
1384 /*** IAMDirectSound methods ***/
1385 static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface, IDirectSound **ds)
1387 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1389 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1394 static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1396 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1398 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1403 static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1405 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1407 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1412 static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
1414 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1416 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1421 static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1423 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1425 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1430 static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1432 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1434 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1439 static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgsilent)
1441 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1443 FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgsilent);
1448 static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND hwnd)
1450 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1452 FIXME("(%p/%p)->(%p): stub\n", This, iface, hwnd);
1457 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
1459 AMDirectSound_QueryInterface,
1460 AMDirectSound_AddRef,
1461 AMDirectSound_Release,
1462 AMDirectSound_GetDirectSoundInterface,
1463 AMDirectSound_GetPrimaryBufferInterface,
1464 AMDirectSound_GetSecondaryBufferInterface,
1465 AMDirectSound_ReleaseDirectSoundInterface,
1466 AMDirectSound_ReleasePrimaryBufferInterface,
1467 AMDirectSound_ReleaseSecondaryBufferInterface,
1468 AMDirectSound_SetFocusWindow,
1469 AMDirectSound_GetFocusWindow