msvcp90: Added numpunct<wchar> implementation.
[wine.git] / dlls / quartz / dsoundrender.c
blob6e51cc4574de5f6a0f2c4265a33f58a9d18f5503
1 /*
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
21 #include "config.h"
23 #include "quartz_private.h"
24 #include "pin.h"
26 #include "uuids.h"
27 #include "vfwmsgs.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "dshow.h"
31 #include "evcode.h"
32 #include "strmif.h"
33 #include "dsound.h"
34 #include "amaudio.h"
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
41 /* NOTE: buffer can still be filled completely,
42 * but we start waiting until only this amount is buffered
44 static const REFERENCE_TIME DSoundRenderer_Max_Fill = 150 * 10000;
46 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
48 static const IBaseFilterVtbl DSoundRender_Vtbl;
49 static const IPinVtbl DSoundRender_InputPin_Vtbl;
50 static const IBasicAudioVtbl IBasicAudio_Vtbl;
51 static const IReferenceClockVtbl IReferenceClock_Vtbl;
52 static const IMediaSeekingVtbl IMediaSeeking_Vtbl;
53 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl;
54 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl;
55 static const IQualityControlVtbl DSoundRender_QualityControl_Vtbl = {
56 QualityControlImpl_QueryInterface,
57 QualityControlImpl_AddRef,
58 QualityControlImpl_Release,
59 QualityControlImpl_Notify,
60 QualityControlImpl_SetSink
63 typedef struct DSoundRenderImpl
65 BaseFilter filter;
67 const IBasicAudioVtbl *IBasicAudio_vtbl;
68 const IReferenceClockVtbl *IReferenceClock_vtbl;
69 const IAMDirectSoundVtbl *IAMDirectSound_vtbl;
70 const IAMFilterMiscFlagsVtbl *IAMFilterMiscFlags_vtbl;
71 IUnknown *seekthru_unk;
73 BaseInputPin * pInputPin;
74 QualityControlImpl qcimpl;
76 IDirectSound8 *dsound;
77 LPDIRECTSOUNDBUFFER dsbuffer;
78 DWORD buf_size;
79 DWORD in_loop;
80 DWORD last_playpos, writepos;
82 REFERENCE_TIME play_time;
84 HANDLE state_change, blocked;
86 LONG volume;
87 LONG pan;
89 DWORD threadid;
90 HANDLE advisethread, thread_wait;
91 } DSoundRenderImpl;
93 static REFERENCE_TIME time_from_pos(DSoundRenderImpl *This, DWORD pos) {
94 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
95 REFERENCE_TIME ret = 10000000;
96 ret = ret * pos / wfx->nAvgBytesPerSec;
97 return ret;
100 static DWORD pos_from_time(DSoundRenderImpl *This, REFERENCE_TIME time) {
101 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
102 REFERENCE_TIME ret = time;
103 ret *= wfx->nSamplesPerSec;
104 ret /= 10000000;
105 ret *= wfx->nBlockAlign;
106 return ret;
109 static void DSoundRender_UpdatePositions(DSoundRenderImpl *This, DWORD *seqwritepos, DWORD *minwritepos) {
110 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
111 BYTE *buf1, *buf2;
112 DWORD size1, size2, playpos, writepos, old_writepos, old_playpos, adv;
113 BOOL writepos_set = This->writepos < This->buf_size;
115 /* Update position and zero */
116 old_writepos = This->writepos;
117 old_playpos = This->last_playpos;
118 if (old_writepos <= old_playpos)
119 old_writepos += This->buf_size;
121 IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, &playpos, &writepos);
122 if (old_playpos > playpos) {
123 adv = This->buf_size + playpos - old_playpos;
124 This->play_time += time_from_pos(This, This->buf_size);
125 } else
126 adv = playpos - old_playpos;
127 This->last_playpos = playpos;
128 if (adv) {
129 TRACE("Moving from %u to %u: clearing %u bytes\n", old_playpos, playpos, adv);
130 IDirectSoundBuffer_Lock(This->dsbuffer, old_playpos, adv, (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
131 memset(buf1, wfx->wBitsPerSample == 8 ? 128 : 0, size1);
132 memset(buf2, wfx->wBitsPerSample == 8 ? 128 : 0, size2);
133 IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
135 *minwritepos = writepos;
136 if (!writepos_set || old_writepos < writepos) {
137 if (writepos_set) {
138 This->writepos = This->buf_size;
139 FIXME("Underrun of data occurred!\n");
141 *seqwritepos = writepos;
142 } else
143 *seqwritepos = This->writepos;
146 static HRESULT DSoundRender_GetWritePos(DSoundRenderImpl *This, DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip)
148 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->pInputPin->pin.mtCurrent.pbFormat;
149 DWORD writepos, min_writepos, playpos;
150 REFERENCE_TIME max_lag = 50 * 10000;
151 REFERENCE_TIME min_lag = 25 * 10000;
152 REFERENCE_TIME cur, writepos_t, delta_t;
154 DSoundRender_UpdatePositions(This, &writepos, &min_writepos);
155 playpos = This->last_playpos;
156 if (This->filter.pClock == (IReferenceClock*)&This->IReferenceClock_vtbl) {
157 max_lag = min_lag;
158 cur = This->play_time + time_from_pos(This, playpos);
159 cur -= This->filter.rtStreamStart;
160 } else if (This->filter.pClock) {
161 IReferenceClock_GetTime(This->filter.pClock, &cur);
162 cur -= This->filter.rtStreamStart;
163 } else
164 write_at = -1;
166 if (writepos == min_writepos)
167 max_lag = 0;
169 *skip = 0;
170 if (write_at < 0) {
171 *ret_writepos = writepos;
172 goto end;
175 if (writepos >= playpos)
176 writepos_t = cur + time_from_pos(This, writepos - playpos);
177 else
178 writepos_t = cur + time_from_pos(This, This->buf_size + writepos - playpos);
180 /* write_at: Starting time of sample */
181 /* cur: current time of play position */
182 /* writepos_t: current time of our pointer play position */
183 delta_t = write_at - writepos_t;
184 if (delta_t >= -max_lag && delta_t <= max_lag) {
185 TRACE("Continuing from old position\n");
186 *ret_writepos = writepos;
187 } else if (delta_t < 0) {
188 REFERENCE_TIME past, min_writepos_t;
189 WARN("Delta too big %i/%i, overwriting old data or even skipping\n", (int)delta_t / 10000, (int)max_lag / 10000);
190 if (min_writepos >= playpos)
191 min_writepos_t = cur + time_from_pos(This, min_writepos - playpos);
192 else
193 min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos);
194 past = min_writepos_t - write_at;
195 if (past >= 0) {
196 DWORD skipbytes = pos_from_time(This, past);
197 WARN("Skipping %u bytes\n", skipbytes);
198 *skip = skipbytes;
199 *ret_writepos = min_writepos;
200 } else {
201 DWORD aheadbytes = pos_from_time(This, -past);
202 WARN("Advancing %u bytes\n", aheadbytes);
203 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
205 } else /* delta_t > 0 */ {
206 DWORD aheadbytes;
207 WARN("Delta too big %i/%i, too far ahead\n", (int)delta_t / 10000, (int)max_lag / 10000);
208 aheadbytes = pos_from_time(This, delta_t);
209 WARN("Advancing %u bytes\n", aheadbytes);
210 if (delta_t >= DSoundRenderer_Max_Fill)
211 return S_FALSE;
212 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
214 end:
215 if (playpos > *ret_writepos)
216 *pfree = playpos - *ret_writepos;
217 else if (playpos == *ret_writepos)
218 *pfree = This->buf_size - wfx->nBlockAlign;
219 else
220 *pfree = This->buf_size + playpos - *ret_writepos;
221 if (time_from_pos(This, This->buf_size - *pfree) >= DSoundRenderer_Max_Fill) {
222 TRACE("Blocked: too full %i / %i\n", (int)(time_from_pos(This, This->buf_size - *pfree)/10000), (int)(DSoundRenderer_Max_Fill / 10000));
223 return S_FALSE;
225 return S_OK;
228 static HRESULT DSoundRender_HandleEndOfStream(DSoundRenderImpl *This)
230 HRESULT hr;
231 IMediaEventSink *pEventSink;
233 while (1)
235 DWORD pos1, pos2;
236 DSoundRender_UpdatePositions(This, &pos1, &pos2);
237 if (pos1 == pos2)
238 break;
240 This->in_loop = 1;
241 LeaveCriticalSection(&This->filter.csFilter);
242 WaitForSingleObject(This->blocked, 10);
243 EnterCriticalSection(&This->filter.csFilter);
244 This->in_loop = 0;
245 if (This->pInputPin->flushing ||
246 This->filter.state != State_Running) {
247 SetEvent(This->state_change);
248 return S_FALSE;
252 if (!This->filter.filterInfo.pGraph)
253 return S_OK;
255 hr = IFilterGraph_QueryInterface(This->filter.filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
256 if (SUCCEEDED(hr))
258 hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, (LONG_PTR)This);
259 IMediaEventSink_Release(pEventSink);
261 return hr;
264 static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* This, REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size)
266 HRESULT hr;
268 while (size && This->filter.state != State_Stopped) {
269 DWORD writepos, skip = 0, free, size1, size2, ret;
270 BYTE *buf1, *buf2;
272 if (This->filter.state == State_Running)
273 hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
274 else
275 hr = S_FALSE;
277 if (hr != S_OK) {
278 This->in_loop = 1;
279 LeaveCriticalSection(&This->filter.csFilter);
280 ret = WaitForSingleObject(This->blocked, 10);
281 EnterCriticalSection(&This->filter.csFilter);
282 This->in_loop = 0;
283 if (This->pInputPin->flushing ||
284 This->filter.state == State_Stopped) {
285 SetEvent(This->state_change);
286 return This->filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE;
288 if (ret != WAIT_TIMEOUT)
289 ERR("%x\n", ret);
290 continue;
292 tStart = -1;
294 if (skip)
295 FIXME("Sample dropped %u of %u bytes\n", skip, size);
296 if (skip >= size)
297 return S_OK;
298 data += skip;
299 size -= skip;
301 hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
302 if (hr != DS_OK) {
303 ERR("Unable to lock sound buffer! (%x)\n", hr);
304 break;
306 memcpy(buf1, data, size1);
307 if (size2)
308 memcpy(buf2, data+size1, size2);
309 IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
310 This->writepos = (writepos + size1 + size2) % This->buf_size;
311 TRACE("Wrote %u bytes at %u, next at %u - (%u/%u)\n", size1+size2, writepos, This->writepos, free, size);
312 data += size1 + size2;
313 size -= size1 + size2;
315 return S_OK;
318 static HRESULT WINAPI DSoundRender_Receive(BaseInputPin *pin, IMediaSample * pSample)
320 DSoundRenderImpl *This = (DSoundRenderImpl*)pin->pin.pinInfo.pFilter;
321 LPBYTE pbSrcStream = NULL;
322 LONG cbSrcStream = 0;
323 REFERENCE_TIME tStart, tStop;
324 HRESULT hr;
325 AM_MEDIA_TYPE *amt;
327 TRACE("%p %p\n", pin, pSample);
329 /* Slightly incorrect, Pause completes when a frame is received so we should signal
330 * pause completion here, but for sound playing a single frame doesn't make sense
333 EnterCriticalSection(&This->filter.csFilter);
335 if (This->pInputPin->end_of_stream || This->pInputPin->flushing)
337 LeaveCriticalSection(&This->filter.csFilter);
338 return S_FALSE;
341 if (This->filter.state == State_Stopped)
343 LeaveCriticalSection(&This->filter.csFilter);
344 return VFW_E_WRONG_STATE;
347 if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
349 AM_MEDIA_TYPE *orig = &This->pInputPin->pin.mtCurrent;
350 WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
351 WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
353 if (origfmt->wFormatTag == newfmt->wFormatTag &&
354 origfmt->nChannels == newfmt->nChannels &&
355 origfmt->nBlockAlign == newfmt->nBlockAlign &&
356 origfmt->wBitsPerSample == newfmt->wBitsPerSample &&
357 origfmt->cbSize == newfmt->cbSize)
359 if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec)
361 hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer,
362 newfmt->nSamplesPerSec);
363 if (FAILED(hr))
365 LeaveCriticalSection(&This->filter.csFilter);
366 return VFW_E_TYPE_NOT_ACCEPTED;
368 FreeMediaType(orig);
369 CopyMediaType(orig, amt);
370 IMediaSample_SetMediaType(pSample, NULL);
373 else
375 LeaveCriticalSection(&This->filter.csFilter);
376 return VFW_E_TYPE_NOT_ACCEPTED;
380 hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
381 if (FAILED(hr))
383 ERR("Cannot get pointer to sample data (%x)\n", hr);
384 LeaveCriticalSection(&This->filter.csFilter);
385 return hr;
388 if (IMediaSample_GetMediaTime(pSample, &tStart, &tStop) == S_OK)
389 RendererPosPassThru_RegisterMediaTime(This->seekthru_unk, tStart);
390 hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
391 if (FAILED(hr)) {
392 ERR("Cannot get sample time (%x)\n", hr);
393 tStart = tStop = -1;
396 IMediaSample_IsDiscontinuity(pSample);
398 if (IMediaSample_IsPreroll(pSample) == S_OK)
400 TRACE("Preroll!\n");
401 LeaveCriticalSection(&This->filter.csFilter);
402 return S_OK;
405 cbSrcStream = IMediaSample_GetActualDataLength(pSample);
406 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
408 SetEvent(This->state_change);
409 hr = DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream);
410 if (This->filter.state == State_Running && This->filter.pClock && tStart >= 0) {
411 REFERENCE_TIME jitter, now = 0;
412 Quality q;
413 IReferenceClock_GetTime(This->filter.pClock, &now);
414 jitter = now - This->filter.rtStreamStart - tStart;
415 if (jitter <= -DSoundRenderer_Max_Fill)
416 jitter += DSoundRenderer_Max_Fill;
417 else if (jitter < 0)
418 jitter = 0;
419 q.Type = (jitter > 0 ? Famine : Flood);
420 q.Proportion = 1.;
421 q.Late = jitter;
422 q.TimeStamp = tStart;
423 IQualityControl_Notify((IQualityControl *)&This->qcimpl, (IBaseFilter*)This, q);
425 LeaveCriticalSection(&This->filter.csFilter);
426 return hr;
429 static HRESULT WINAPI DSoundRender_CheckMediaType(BasePin *iface, const AM_MEDIA_TYPE * pmt)
431 WAVEFORMATEX* format;
433 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
434 return S_FALSE;
436 format = (WAVEFORMATEX*)pmt->pbFormat;
437 TRACE("Format = %p\n", format);
438 TRACE("wFormatTag = %x %x\n", format->wFormatTag, WAVE_FORMAT_PCM);
439 TRACE("nChannels = %d\n", format->nChannels);
440 TRACE("nSamplesPerSec = %d\n", format->nAvgBytesPerSec);
441 TRACE("nAvgBytesPerSec = %d\n", format->nAvgBytesPerSec);
442 TRACE("nBlockAlign = %d\n", format->nBlockAlign);
443 TRACE("wBitsPerSample = %d\n", format->wBitsPerSample);
445 if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
446 return S_FALSE;
448 return S_OK;
451 static IPin* WINAPI DSoundRender_GetPin(BaseFilter *iface, int pos)
453 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
455 if (pos >= 1 || pos < 0)
456 return NULL;
458 IPin_AddRef((IPin*)This->pInputPin);
459 return (IPin*)This->pInputPin;
462 static LONG WINAPI DSoundRender_GetPinCount(BaseFilter *iface)
464 /* Our pins are static */
465 return 1;
468 static const BaseFilterFuncTable BaseFuncTable = {
469 DSoundRender_GetPin,
470 DSoundRender_GetPinCount
473 static const BasePinFuncTable input_BaseFuncTable = {
474 DSoundRender_CheckMediaType,
475 NULL,
476 BasePinImpl_GetMediaTypeVersion,
477 BasePinImpl_GetMediaType
480 static const BaseInputPinFuncTable input_BaseInputFuncTable = {
481 DSoundRender_Receive
485 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv)
487 HRESULT hr;
488 PIN_INFO piInput;
489 DSoundRenderImpl * pDSoundRender;
491 TRACE("(%p, %p)\n", pUnkOuter, ppv);
493 *ppv = NULL;
495 if (pUnkOuter)
496 return CLASS_E_NOAGGREGATION;
498 pDSoundRender = CoTaskMemAlloc(sizeof(DSoundRenderImpl));
499 if (!pDSoundRender)
500 return E_OUTOFMEMORY;
501 ZeroMemory(pDSoundRender, sizeof(DSoundRenderImpl));
503 BaseFilter_Init(&pDSoundRender->filter, &DSoundRender_Vtbl, &CLSID_DSoundRender, (DWORD_PTR)(__FILE__ ": DSoundRenderImpl.csFilter"), &BaseFuncTable);
505 pDSoundRender->IBasicAudio_vtbl = &IBasicAudio_Vtbl;
506 pDSoundRender->IReferenceClock_vtbl = &IReferenceClock_Vtbl;
507 pDSoundRender->IAMDirectSound_vtbl = &IAMDirectSound_Vtbl;
508 pDSoundRender->IAMFilterMiscFlags_vtbl = &IAMFilterMiscFlags_Vtbl;
510 /* construct input pin */
511 piInput.dir = PINDIR_INPUT;
512 piInput.pFilter = (IBaseFilter *)pDSoundRender;
513 lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
514 hr = BaseInputPin_Construct(&DSoundRender_InputPin_Vtbl, &piInput, &input_BaseFuncTable, &input_BaseInputFuncTable, &pDSoundRender->filter.csFilter, NULL, (IPin **)&pDSoundRender->pInputPin);
516 if (SUCCEEDED(hr))
518 hr = DirectSoundCreate8(NULL, &pDSoundRender->dsound, NULL);
519 if (FAILED(hr))
520 ERR("Cannot create Direct Sound object (%x)\n", hr);
521 else
522 hr = IDirectSound_SetCooperativeLevel(pDSoundRender->dsound, GetDesktopWindow(), DSSCL_PRIORITY);
523 if (SUCCEEDED(hr)) {
524 IDirectSoundBuffer *buf;
525 DSBUFFERDESC buf_desc;
526 memset(&buf_desc,0,sizeof(DSBUFFERDESC));
527 buf_desc.dwSize = sizeof(DSBUFFERDESC);
528 buf_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
529 hr = IDirectSound_CreateSoundBuffer(pDSoundRender->dsound, &buf_desc, &buf, NULL);
530 if (SUCCEEDED(hr)) {
531 IDirectSoundBuffer_Play(buf, 0, 0, DSBPLAY_LOOPING);
532 IUnknown_Release(buf);
534 hr = S_OK;
538 if (SUCCEEDED(hr))
540 pDSoundRender->state_change = CreateEventW(NULL, TRUE, TRUE, NULL);
541 pDSoundRender->blocked = CreateEventW(NULL, TRUE, TRUE, NULL);
543 hr = CreatePosPassThru((IUnknown*)pDSoundRender, TRUE, (IPin*)pDSoundRender->pInputPin, &pDSoundRender->seekthru_unk);
545 if (!pDSoundRender->state_change || !pDSoundRender->blocked || FAILED(hr))
547 IUnknown_Release((IUnknown *)pDSoundRender);
548 return HRESULT_FROM_WIN32(GetLastError());
551 QualityControlImpl_init(&pDSoundRender->qcimpl, (IPin*)pDSoundRender->pInputPin, (IBaseFilter*)pDSoundRender);
552 pDSoundRender->qcimpl.lpVtbl = &DSoundRender_QualityControl_Vtbl;
553 *ppv = pDSoundRender;
555 else
557 if (pDSoundRender->pInputPin)
558 IPin_Release((IPin*)pDSoundRender->pInputPin);
559 BaseFilterImpl_Release((IBaseFilter*)pDSoundRender);
560 CoTaskMemFree(pDSoundRender);
563 return hr;
566 static HRESULT WINAPI DSoundRender_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
568 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
569 TRACE("(%p, %p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
571 *ppv = NULL;
573 if (IsEqualIID(riid, &IID_IUnknown))
574 *ppv = This;
575 else if (IsEqualIID(riid, &IID_IPersist))
576 *ppv = This;
577 else if (IsEqualIID(riid, &IID_IMediaFilter))
578 *ppv = This;
579 else if (IsEqualIID(riid, &IID_IBaseFilter))
580 *ppv = This;
581 else if (IsEqualIID(riid, &IID_IBasicAudio))
582 *ppv = &This->IBasicAudio_vtbl;
583 else if (IsEqualIID(riid, &IID_IReferenceClock))
584 *ppv = &This->IReferenceClock_vtbl;
585 else if (IsEqualIID(riid, &IID_IMediaSeeking))
586 return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
587 else if (IsEqualIID(riid, &IID_IAMDirectSound))
588 *ppv = &This->IAMDirectSound_vtbl;
589 else if (IsEqualIID(riid, &IID_IAMFilterMiscFlags))
590 *ppv = &This->IAMFilterMiscFlags_vtbl;
591 else if (IsEqualIID(riid, &IID_IQualityControl))
592 *ppv = &This->qcimpl;
594 if (*ppv)
596 IUnknown_AddRef((IUnknown *)(*ppv));
597 return S_OK;
600 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
601 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
603 return E_NOINTERFACE;
606 static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface)
608 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
609 ULONG refCount = BaseFilterImpl_Release(iface);
611 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
613 if (!refCount)
615 IPin *pConnectedTo;
617 if (This->threadid) {
618 PostThreadMessageW(This->threadid, WM_APP, 0, 0);
619 WaitForSingleObject(This->advisethread, INFINITE);
620 CloseHandle(This->advisethread);
623 if (This->dsbuffer)
624 IDirectSoundBuffer_Release(This->dsbuffer);
625 This->dsbuffer = NULL;
626 if (This->dsound)
627 IDirectSound_Release(This->dsound);
628 This->dsound = NULL;
629 if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo)))
631 IPin_Disconnect(pConnectedTo);
632 IPin_Release(pConnectedTo);
634 IPin_Disconnect((IPin *)This->pInputPin);
636 IPin_Release((IPin *)This->pInputPin);
638 This->IBasicAudio_vtbl = NULL;
639 if (This->seekthru_unk)
640 IUnknown_Release(This->seekthru_unk);
642 CloseHandle(This->state_change);
643 CloseHandle(This->blocked);
645 TRACE("Destroying Audio Renderer\n");
646 CoTaskMemFree(This);
648 return 0;
650 else
651 return refCount;
654 /** IMediaFilter methods **/
656 static HRESULT WINAPI DSoundRender_Stop(IBaseFilter * iface)
658 HRESULT hr = S_OK;
659 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
661 TRACE("(%p/%p)->()\n", This, iface);
663 EnterCriticalSection(&This->filter.csFilter);
665 hr = IDirectSoundBuffer_Stop(This->dsbuffer);
666 if (SUCCEEDED(hr))
667 This->filter.state = State_Stopped;
669 /* Complete our transition */
670 This->writepos = This->buf_size;
671 SetEvent(This->state_change);
672 SetEvent(This->blocked);
673 RendererPosPassThru_ResetMediaTime(This->seekthru_unk);
675 LeaveCriticalSection(&This->filter.csFilter);
677 return hr;
680 static HRESULT WINAPI DSoundRender_Pause(IBaseFilter * iface)
682 HRESULT hr = S_OK;
683 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
685 TRACE("(%p/%p)->()\n", This, iface);
687 EnterCriticalSection(&This->filter.csFilter);
688 if (This->filter.state != State_Paused)
690 if (This->filter.state == State_Stopped)
692 This->pInputPin->end_of_stream = 0;
693 ResetEvent(This->state_change);
696 hr = IDirectSoundBuffer_Stop(This->dsbuffer);
697 if (SUCCEEDED(hr))
698 This->filter.state = State_Paused;
700 ResetEvent(This->blocked);
702 LeaveCriticalSection(&This->filter.csFilter);
704 return hr;
707 static HRESULT WINAPI DSoundRender_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
709 HRESULT hr = S_OK;
710 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
712 TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
714 EnterCriticalSection(&This->filter.csFilter);
715 if (This->pInputPin->pin.pConnectedTo)
717 This->filter.rtStreamStart = tStart;
718 QualityControlRender_Start(&This->qcimpl, tStart);
719 if (This->filter.state == State_Paused)
721 /* Unblock our thread, state changing from paused to running doesn't need a reset for state change */
722 SetEvent(This->blocked);
724 else if (This->filter.state == State_Stopped)
726 ResetEvent(This->state_change);
727 This->pInputPin->end_of_stream = 0;
729 IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
730 ResetEvent(This->blocked);
731 } else if (This->filter.filterInfo.pGraph) {
732 IMediaEventSink *pEventSink;
733 hr = IFilterGraph_QueryInterface(This->filter.filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
734 if (SUCCEEDED(hr))
736 hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, (LONG_PTR)This);
737 IMediaEventSink_Release(pEventSink);
739 hr = S_OK;
741 if (SUCCEEDED(hr))
742 This->filter.state = State_Running;
743 LeaveCriticalSection(&This->filter.csFilter);
745 return hr;
748 static HRESULT WINAPI DSoundRender_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
750 HRESULT hr;
751 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
753 TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
755 if (WaitForSingleObject(This->state_change, dwMilliSecsTimeout) == WAIT_TIMEOUT)
756 hr = VFW_S_STATE_INTERMEDIATE;
757 else
758 hr = S_OK;
760 BaseFilterImpl_GetState(iface, dwMilliSecsTimeout, pState);
762 return hr;
765 /** IBaseFilter implementation **/
767 static HRESULT WINAPI DSoundRender_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
769 DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
771 TRACE("(%p/%p)->(%s,%p)\n", This, iface, debugstr_w(Id), ppPin);
773 FIXME("DSoundRender::FindPin(...)\n");
775 /* FIXME: critical section */
777 return E_NOTIMPL;
780 static const IBaseFilterVtbl DSoundRender_Vtbl =
782 DSoundRender_QueryInterface,
783 BaseFilterImpl_AddRef,
784 DSoundRender_Release,
785 BaseFilterImpl_GetClassID,
786 DSoundRender_Stop,
787 DSoundRender_Pause,
788 DSoundRender_Run,
789 DSoundRender_GetState,
790 BaseFilterImpl_SetSyncSource,
791 BaseFilterImpl_GetSyncSource,
792 BaseFilterImpl_EnumPins,
793 DSoundRender_FindPin,
794 BaseFilterImpl_QueryFilterInfo,
795 BaseFilterImpl_JoinFilterGraph,
796 BaseFilterImpl_QueryVendorInfo
799 static HRESULT WINAPI DSoundRender_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
801 BaseInputPin *This = (BaseInputPin *)iface;
802 PIN_DIRECTION pindirReceive;
803 DSoundRenderImpl *DSImpl;
804 HRESULT hr = S_OK;
806 TRACE("(%p)->(%p, %p)\n", This, pReceivePin, pmt);
807 dump_AM_MEDIA_TYPE(pmt);
809 EnterCriticalSection(This->pin.pCritSec);
811 DSImpl = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
813 if (This->pin.pConnectedTo)
814 hr = VFW_E_ALREADY_CONNECTED;
816 if (SUCCEEDED(hr) && This->pin.pFuncsTable->pfnCheckMediaType((BasePin*)This, pmt) != S_OK)
817 hr = VFW_E_TYPE_NOT_ACCEPTED;
819 if (SUCCEEDED(hr))
821 IPin_QueryDirection(pReceivePin, &pindirReceive);
823 if (pindirReceive != PINDIR_OUTPUT)
825 ERR("Can't connect from non-output pin\n");
826 hr = VFW_E_INVALID_DIRECTION;
830 if (SUCCEEDED(hr))
832 WAVEFORMATEX *format;
833 DSBUFFERDESC buf_desc;
835 TRACE("MajorType %s\n", debugstr_guid(&pmt->majortype));
836 TRACE("SubType %s\n", debugstr_guid(&pmt->subtype));
837 TRACE("Format %s\n", debugstr_guid(&pmt->formattype));
838 TRACE("Size %d\n", pmt->cbFormat);
840 format = (WAVEFORMATEX*)pmt->pbFormat;
842 DSImpl->buf_size = format->nAvgBytesPerSec;
844 memset(&buf_desc,0,sizeof(DSBUFFERDESC));
845 buf_desc.dwSize = sizeof(DSBUFFERDESC);
846 buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
847 DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS |
848 DSBCAPS_GETCURRENTPOSITION2;
849 buf_desc.dwBufferBytes = DSImpl->buf_size;
850 buf_desc.lpwfxFormat = format;
851 hr = IDirectSound_CreateSoundBuffer(DSImpl->dsound, &buf_desc, &DSImpl->dsbuffer, NULL);
852 DSImpl->writepos = DSImpl->buf_size;
853 if (FAILED(hr))
854 ERR("Can't create sound buffer (%x)\n", hr);
857 if (SUCCEEDED(hr))
859 hr = IDirectSoundBuffer_SetVolume(DSImpl->dsbuffer, DSImpl->volume);
860 if (FAILED(hr))
861 ERR("Can't set volume to %d (%x)\n", DSImpl->volume, hr);
863 hr = IDirectSoundBuffer_SetPan(DSImpl->dsbuffer, DSImpl->pan);
864 if (FAILED(hr))
865 ERR("Can't set pan to %d (%x)\n", DSImpl->pan, hr);
866 hr = S_OK;
869 if (SUCCEEDED(hr))
871 CopyMediaType(&This->pin.mtCurrent, pmt);
872 This->pin.pConnectedTo = pReceivePin;
873 IPin_AddRef(pReceivePin);
875 else if (hr != VFW_E_ALREADY_CONNECTED)
877 if (DSImpl->dsbuffer)
878 IDirectSoundBuffer_Release(DSImpl->dsbuffer);
879 DSImpl->dsbuffer = NULL;
882 LeaveCriticalSection(This->pin.pCritSec);
884 return hr;
887 static HRESULT WINAPI DSoundRender_InputPin_Disconnect(IPin * iface)
889 BasePin *This = (BasePin*)iface;
890 DSoundRenderImpl *DSImpl;
892 TRACE("(%p)->()\n", iface);
894 DSImpl = (DSoundRenderImpl*)This->pinInfo.pFilter;
895 if (DSImpl->threadid) {
896 PostThreadMessageW(DSImpl->threadid, WM_APP, 0, 0);
897 WaitForSingleObject(DSImpl->advisethread, INFINITE);
898 CloseHandle(DSImpl->advisethread);
900 if (DSImpl->dsbuffer)
901 IDirectSoundBuffer_Release(DSImpl->dsbuffer);
902 DSImpl->dsbuffer = NULL;
904 return BasePinImpl_Disconnect(iface);
907 static HRESULT WINAPI DSoundRender_InputPin_EndOfStream(IPin * iface)
909 BaseInputPin* This = (BaseInputPin*)iface;
910 DSoundRenderImpl *me = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
911 HRESULT hr;
913 EnterCriticalSection(This->pin.pCritSec);
915 TRACE("(%p/%p)->()\n", This, iface);
916 hr = BaseInputPinImpl_EndOfStream(iface);
917 if (hr != S_OK)
919 ERR("%08x\n", hr);
920 LeaveCriticalSection(This->pin.pCritSec);
921 return hr;
924 hr = DSoundRender_HandleEndOfStream(me);
925 RendererPosPassThru_EOS(me->seekthru_unk);
926 SetEvent(me->state_change);
927 LeaveCriticalSection(This->pin.pCritSec);
929 return hr;
932 static HRESULT WINAPI DSoundRender_InputPin_BeginFlush(IPin * iface)
934 BaseInputPin *This = (BaseInputPin *)iface;
935 DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
936 HRESULT hr;
938 TRACE("\n");
940 EnterCriticalSection(This->pin.pCritSec);
941 hr = BaseInputPinImpl_BeginFlush(iface);
942 SetEvent(pFilter->blocked);
943 LeaveCriticalSection(This->pin.pCritSec);
945 return hr;
948 static HRESULT WINAPI DSoundRender_InputPin_EndFlush(IPin * iface)
950 BaseInputPin *This = (BaseInputPin *)iface;
951 DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
952 HRESULT hr;
954 TRACE("\n");
956 EnterCriticalSection(This->pin.pCritSec);
957 if (pFilter->in_loop) {
958 ResetEvent(pFilter->state_change);
959 LeaveCriticalSection(This->pin.pCritSec);
960 WaitForSingleObject(pFilter->state_change, -1);
961 EnterCriticalSection(This->pin.pCritSec);
963 if (pFilter->filter.state != State_Stopped)
964 ResetEvent(pFilter->blocked);
966 if (pFilter->dsbuffer)
968 LPBYTE buffer;
969 DWORD size;
971 /* Force a reset */
972 IDirectSoundBuffer_Lock(pFilter->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
973 memset(buffer, 0, size);
974 IDirectSoundBuffer_Unlock(pFilter->dsbuffer, buffer, size, NULL, 0);
975 pFilter->writepos = pFilter->buf_size;
977 QualityControlRender_Start(&pFilter->qcimpl, pFilter->filter.rtStreamStart);
978 hr = BaseInputPinImpl_EndFlush(iface);
979 LeaveCriticalSection(This->pin.pCritSec);
980 RendererPosPassThru_ResetMediaTime(pFilter->seekthru_unk);
982 return hr;
985 static const IPinVtbl DSoundRender_InputPin_Vtbl =
987 BaseInputPinImpl_QueryInterface,
988 BasePinImpl_AddRef,
989 BaseInputPinImpl_Release,
990 BaseInputPinImpl_Connect,
991 DSoundRender_InputPin_ReceiveConnection,
992 DSoundRender_InputPin_Disconnect,
993 BasePinImpl_ConnectedTo,
994 BasePinImpl_ConnectionMediaType,
995 BasePinImpl_QueryPinInfo,
996 BasePinImpl_QueryDirection,
997 BasePinImpl_QueryId,
998 BaseInputPinImpl_QueryAccept,
999 BasePinImpl_EnumMediaTypes,
1000 BasePinImpl_QueryInternalConnections,
1001 DSoundRender_InputPin_EndOfStream,
1002 DSoundRender_InputPin_BeginFlush,
1003 DSoundRender_InputPin_EndFlush,
1004 BaseInputPinImpl_NewSegment
1007 /*** IUnknown methods ***/
1008 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
1009 REFIID riid,
1010 LPVOID*ppvObj) {
1011 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1013 TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1015 return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1018 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
1019 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1021 TRACE("(%p/%p)->()\n", This, iface);
1023 return BaseFilterImpl_AddRef((IBaseFilter*)This);
1026 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
1027 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1029 TRACE("(%p/%p)->()\n", This, iface);
1031 return DSoundRender_Release((IBaseFilter*)This);
1034 /*** IDispatch methods ***/
1035 static HRESULT WINAPI Basicaudio_GetTypeInfoCount(IBasicAudio *iface,
1036 UINT*pctinfo) {
1037 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1039 TRACE("(%p/%p)->(%p): stub !!!\n", This, iface, pctinfo);
1041 return S_OK;
1044 static HRESULT WINAPI Basicaudio_GetTypeInfo(IBasicAudio *iface,
1045 UINT iTInfo,
1046 LCID lcid,
1047 ITypeInfo**ppTInfo) {
1048 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1050 TRACE("(%p/%p)->(%d, %d, %p): stub !!!\n", This, iface, iTInfo, lcid, ppTInfo);
1052 return S_OK;
1055 static HRESULT WINAPI Basicaudio_GetIDsOfNames(IBasicAudio *iface,
1056 REFIID riid,
1057 LPOLESTR*rgszNames,
1058 UINT cNames,
1059 LCID lcid,
1060 DISPID*rgDispId) {
1061 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1063 TRACE("(%p/%p)->(%s (%p), %p, %d, %d, %p): stub !!!\n", This, iface, debugstr_guid(riid), riid, rgszNames, cNames, lcid, rgDispId);
1065 return S_OK;
1068 static HRESULT WINAPI Basicaudio_Invoke(IBasicAudio *iface,
1069 DISPID dispIdMember,
1070 REFIID riid,
1071 LCID lcid,
1072 WORD wFlags,
1073 DISPPARAMS*pDispParams,
1074 VARIANT*pVarResult,
1075 EXCEPINFO*pExepInfo,
1076 UINT*puArgErr) {
1077 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1079 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);
1081 return S_OK;
1084 /*** IBasicAudio methods ***/
1085 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
1086 LONG lVolume) {
1087 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1089 TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
1091 if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
1092 return E_INVALIDARG;
1094 if (This->dsbuffer) {
1095 if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
1096 return E_FAIL;
1099 This->volume = lVolume;
1100 return S_OK;
1103 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
1104 LONG *plVolume) {
1105 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1107 TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
1109 if (!plVolume)
1110 return E_POINTER;
1112 *plVolume = This->volume;
1113 return S_OK;
1116 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
1117 LONG lBalance) {
1118 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1120 TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
1122 if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
1123 return E_INVALIDARG;
1125 if (This->dsbuffer) {
1126 if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
1127 return E_FAIL;
1130 This->pan = lBalance;
1131 return S_OK;
1134 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
1135 LONG *plBalance) {
1136 ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1138 TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
1140 if (!plBalance)
1141 return E_POINTER;
1143 *plBalance = This->pan;
1144 return S_OK;
1147 static const IBasicAudioVtbl IBasicAudio_Vtbl =
1149 Basicaudio_QueryInterface,
1150 Basicaudio_AddRef,
1151 Basicaudio_Release,
1152 Basicaudio_GetTypeInfoCount,
1153 Basicaudio_GetTypeInfo,
1154 Basicaudio_GetIDsOfNames,
1155 Basicaudio_Invoke,
1156 Basicaudio_put_Volume,
1157 Basicaudio_get_Volume,
1158 Basicaudio_put_Balance,
1159 Basicaudio_get_Balance
1162 struct dsoundrender_timer {
1163 struct dsoundrender_timer *next;
1164 REFERENCE_TIME start;
1165 REFERENCE_TIME periodicity;
1166 HANDLE handle;
1167 DWORD cookie;
1169 static LONG cookie_counter = 1;
1171 static DWORD WINAPI DSoundAdviseThread(LPVOID lpParam) {
1172 DSoundRenderImpl *This = lpParam;
1173 struct dsoundrender_timer head = { };
1174 MSG msg;
1176 TRACE("(%p): Main Loop\n", This);
1178 PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
1179 SetEvent(This->thread_wait);
1181 while (1)
1183 HRESULT hr;
1184 REFERENCE_TIME curtime = 0;
1185 BOOL ret;
1186 struct dsoundrender_timer *prev = &head, *cur;
1188 hr = IReferenceClock_GetTime((IReferenceClock*) &This->IReferenceClock_vtbl, &curtime);
1189 if (FAILED(hr)) {
1190 FIXME("Could not get time: %08x\n", hr);
1191 continue;
1193 TRACE("Time: %s\n", wine_dbgstr_longlong(curtime));
1194 while (prev->next) {
1195 cur = prev->next;
1196 if (cur->start > curtime) {
1197 TRACE("Skipping %p\n", cur);
1198 prev = cur;
1199 } else if (cur->periodicity) {
1200 while (cur->start <= curtime) {
1201 cur->start += cur->periodicity;
1202 ReleaseSemaphore(cur->handle, 1, NULL);
1204 prev = cur;
1205 } else {
1206 struct dsoundrender_timer *next = cur->next;
1207 TRACE("Firing %p %s < %s\n", cur, wine_dbgstr_longlong(cur->start), wine_dbgstr_longlong(curtime));
1208 SetEvent(cur->handle);
1209 HeapFree(GetProcessHeap(), 0, cur);
1210 prev->next = next;
1213 if (!head.next)
1214 ret = GetMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4);
1215 else
1216 ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE);
1217 while (ret) {
1218 switch (LOWORD(msg.message) - WM_APP) {
1219 case 0: TRACE("Exiting\n"); return 0;
1220 case 1:
1221 case 2: {
1222 struct dsoundrender_timer *t = (struct dsoundrender_timer *)msg.wParam;
1223 if (LOWORD(msg.message) - WM_APP == 1)
1224 TRACE("Adding one-shot timer %p\n", t);
1225 else
1226 TRACE("Adding periodic timer %p\n", t);
1227 t->next = head.next;
1228 head.next = t;
1229 break;
1231 case 3:
1232 prev = &head;
1233 while (prev->next) {
1234 cur = prev->next;
1235 if (cur->cookie == msg.wParam) {
1236 struct dsoundrender_timer *next = cur->next;
1237 HeapFree(GetProcessHeap(), 0, cur);
1238 prev->next = next;
1239 break;
1241 prev = cur;
1243 break;
1245 ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE);
1247 MsgWaitForMultipleObjects(0, NULL, 5, QS_POSTMESSAGE, 0);
1249 return 0;
1252 /*** IUnknown methods ***/
1253 static HRESULT WINAPI ReferenceClock_QueryInterface(IReferenceClock *iface,
1254 REFIID riid,
1255 LPVOID*ppvObj)
1257 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1259 TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1261 return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1264 static ULONG WINAPI ReferenceClock_AddRef(IReferenceClock *iface)
1266 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1268 TRACE("(%p/%p)->()\n", This, iface);
1270 return BaseFilterImpl_AddRef((IBaseFilter*)This);
1273 static ULONG WINAPI ReferenceClock_Release(IReferenceClock *iface)
1275 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1277 TRACE("(%p/%p)->()\n", This, iface);
1279 return DSoundRender_Release((IBaseFilter*)This);
1282 /*** IReferenceClock methods ***/
1283 static HRESULT WINAPI ReferenceClock_GetTime(IReferenceClock *iface,
1284 REFERENCE_TIME *pTime)
1286 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1287 HRESULT hr = E_FAIL;
1289 TRACE("(%p/%p)->(%p)\n", This, iface, pTime);
1290 if (!pTime)
1291 return E_POINTER;
1293 if (This->dsbuffer) {
1294 DWORD writepos1, writepos2;
1295 EnterCriticalSection(&This->filter.csFilter);
1296 DSoundRender_UpdatePositions(This, &writepos1, &writepos2);
1297 *pTime = This->play_time + time_from_pos(This, This->last_playpos);
1298 LeaveCriticalSection(&This->filter.csFilter);
1299 hr = S_OK;
1301 if (FAILED(hr))
1302 WARN("Could not get reference time (%x)!\n", hr);
1304 return hr;
1307 static HRESULT WINAPI ReferenceClock_AdviseTime(IReferenceClock *iface,
1308 REFERENCE_TIME rtBaseTime,
1309 REFERENCE_TIME rtStreamTime,
1310 HEVENT hEvent,
1311 DWORD_PTR *pdwAdviseCookie)
1313 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1314 REFERENCE_TIME when = rtBaseTime + rtStreamTime;
1315 REFERENCE_TIME future;
1316 TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hEvent, pdwAdviseCookie);
1318 if (when <= 0)
1319 return E_INVALIDARG;
1321 if (!pdwAdviseCookie)
1322 return E_POINTER;
1324 EnterCriticalSection(&This->filter.csFilter);
1325 future = when - This->play_time;
1326 if (!This->threadid && This->dsbuffer) {
1327 This->thread_wait = CreateEventW(0, 0, 0, 0);
1328 This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid);
1329 WaitForSingleObject(This->thread_wait, INFINITE);
1330 CloseHandle(This->thread_wait);
1332 LeaveCriticalSection(&This->filter.csFilter);
1333 /* If it's in the past or the next millisecond, trigger immediately */
1334 if (future <= 10000) {
1335 SetEvent((HANDLE)hEvent);
1336 *pdwAdviseCookie = 0;
1337 } else {
1338 struct dsoundrender_timer *t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t));
1339 t->next = NULL;
1340 t->start = when;
1341 t->periodicity = 0;
1342 t->handle = (HANDLE)hEvent;
1343 t->cookie = InterlockedIncrement(&cookie_counter);
1344 PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0);
1345 *pdwAdviseCookie = t->cookie;
1348 return S_OK;
1351 static HRESULT WINAPI ReferenceClock_AdvisePeriodic(IReferenceClock *iface,
1352 REFERENCE_TIME rtStartTime,
1353 REFERENCE_TIME rtPeriodTime,
1354 HSEMAPHORE hSemaphore,
1355 DWORD_PTR *pdwAdviseCookie)
1357 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1358 struct dsoundrender_timer *t;
1360 TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtStartTime), wine_dbgstr_longlong(rtPeriodTime), (void*)hSemaphore, pdwAdviseCookie);
1362 if (rtStartTime <= 0 || rtPeriodTime <= 0)
1363 return E_INVALIDARG;
1365 if (!pdwAdviseCookie)
1366 return E_POINTER;
1368 EnterCriticalSection(&This->filter.csFilter);
1369 if (!This->threadid && This->dsbuffer) {
1370 This->thread_wait = CreateEventW(0, 0, 0, 0);
1371 This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid);
1372 WaitForSingleObject(This->thread_wait, INFINITE);
1373 CloseHandle(This->thread_wait);
1375 LeaveCriticalSection(&This->filter.csFilter);
1377 t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t));
1378 t->next = NULL;
1379 t->start = rtStartTime;
1380 t->periodicity = rtPeriodTime;
1381 t->handle = (HANDLE)hSemaphore;
1382 t->cookie = InterlockedIncrement(&cookie_counter);
1383 PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0);
1384 *pdwAdviseCookie = t->cookie;
1386 return S_OK;
1389 static HRESULT WINAPI ReferenceClock_Unadvise(IReferenceClock *iface,
1390 DWORD_PTR dwAdviseCookie)
1392 ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1394 TRACE("(%p/%p)->(%p)\n", This, iface, (void*)dwAdviseCookie);
1395 if (!This->advisethread || !dwAdviseCookie)
1396 return S_FALSE;
1397 PostThreadMessageW(This->threadid, WM_APP+3, dwAdviseCookie, 0);
1398 return S_OK;
1401 static const IReferenceClockVtbl IReferenceClock_Vtbl =
1403 ReferenceClock_QueryInterface,
1404 ReferenceClock_AddRef,
1405 ReferenceClock_Release,
1406 ReferenceClock_GetTime,
1407 ReferenceClock_AdviseTime,
1408 ReferenceClock_AdvisePeriodic,
1409 ReferenceClock_Unadvise
1412 /*** IUnknown methods ***/
1413 static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
1414 REFIID riid,
1415 LPVOID*ppvObj)
1417 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1419 TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1421 return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1424 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
1426 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1428 TRACE("(%p/%p)->()\n", This, iface);
1430 return BaseFilterImpl_AddRef((IBaseFilter*)This);
1433 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
1435 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1437 TRACE("(%p/%p)->()\n", This, iface);
1439 return DSoundRender_Release((IBaseFilter*)This);
1442 /*** IAMDirectSound methods ***/
1443 static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface, IDirectSound **ds)
1445 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1447 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1449 return E_NOTIMPL;
1452 static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1454 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1456 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1458 return E_NOTIMPL;
1461 static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1463 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1465 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1467 return E_NOTIMPL;
1470 static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
1472 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1474 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1476 return E_NOTIMPL;
1479 static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1481 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1483 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1485 return E_NOTIMPL;
1488 static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1490 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1492 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1494 return E_NOTIMPL;
1497 static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgsilent)
1499 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1501 FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgsilent);
1503 return E_NOTIMPL;
1506 static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND hwnd)
1508 ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1510 FIXME("(%p/%p)->(%p): stub\n", This, iface, hwnd);
1512 return E_NOTIMPL;
1515 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
1517 AMDirectSound_QueryInterface,
1518 AMDirectSound_AddRef,
1519 AMDirectSound_Release,
1520 AMDirectSound_GetDirectSoundInterface,
1521 AMDirectSound_GetPrimaryBufferInterface,
1522 AMDirectSound_GetSecondaryBufferInterface,
1523 AMDirectSound_ReleaseDirectSoundInterface,
1524 AMDirectSound_ReleasePrimaryBufferInterface,
1525 AMDirectSound_ReleaseSecondaryBufferInterface,
1526 AMDirectSound_SetFocusWindow,
1527 AMDirectSound_GetFocusWindow
1530 static DSoundRenderImpl *from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface) {
1531 return (DSoundRenderImpl*)((char*)iface - offsetof(DSoundRenderImpl, IAMFilterMiscFlags_vtbl));
1534 static HRESULT WINAPI AMFilterMiscFlags_QueryInterface(IAMFilterMiscFlags *iface, REFIID riid, void **ppv) {
1535 DSoundRenderImpl *This = from_IAMFilterMiscFlags(iface);
1536 return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
1539 static ULONG WINAPI AMFilterMiscFlags_AddRef(IAMFilterMiscFlags *iface) {
1540 DSoundRenderImpl *This = from_IAMFilterMiscFlags(iface);
1541 return IUnknown_AddRef((IUnknown*)This);
1544 static ULONG WINAPI AMFilterMiscFlags_Release(IAMFilterMiscFlags *iface) {
1545 DSoundRenderImpl *This = from_IAMFilterMiscFlags(iface);
1546 return IUnknown_Release((IUnknown*)This);
1549 static ULONG WINAPI AMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags *iface) {
1550 return AM_FILTER_MISC_FLAGS_IS_RENDERER;
1553 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl = {
1554 AMFilterMiscFlags_QueryInterface,
1555 AMFilterMiscFlags_AddRef,
1556 AMFilterMiscFlags_Release,
1557 AMFilterMiscFlags_GetMiscFlags