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 "quartz_private.h" 22 23 /* NOTE: buffer can still be filled completely, 24 * but we start waiting until only this amount is buffered 25 */ 26 static const REFERENCE_TIME DSoundRenderer_Max_Fill = 150 * 10000; 27 28 static const IBaseFilterVtbl DSoundRender_Vtbl; 29 static const IBasicAudioVtbl IBasicAudio_Vtbl; 30 static const IReferenceClockVtbl IReferenceClock_Vtbl; 31 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl; 32 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl; 33 34 typedef struct DSoundRenderImpl 35 { 36 BaseRenderer renderer; 37 BasicAudio basicAudio; 38 39 IReferenceClock IReferenceClock_iface; 40 IAMDirectSound IAMDirectSound_iface; 41 IAMFilterMiscFlags IAMFilterMiscFlags_iface; 42 43 IDirectSound8 *dsound; 44 LPDIRECTSOUNDBUFFER dsbuffer; 45 DWORD buf_size; 46 DWORD in_loop; 47 DWORD last_playpos, writepos; 48 49 REFERENCE_TIME play_time; 50 51 HANDLE blocked; 52 53 LONG volume; 54 LONG pan; 55 56 DWORD threadid; 57 HANDLE advisethread, thread_wait; 58 } DSoundRenderImpl; 59 60 static inline DSoundRenderImpl *impl_from_BaseRenderer(BaseRenderer *iface) 61 { 62 return CONTAINING_RECORD(iface, DSoundRenderImpl, renderer); 63 } 64 65 static inline DSoundRenderImpl *impl_from_IBaseFilter(IBaseFilter *iface) 66 { 67 return CONTAINING_RECORD(iface, DSoundRenderImpl, renderer.filter.IBaseFilter_iface); 68 } 69 70 static inline DSoundRenderImpl *impl_from_IBasicAudio(IBasicAudio *iface) 71 { 72 return CONTAINING_RECORD(iface, DSoundRenderImpl, basicAudio.IBasicAudio_iface); 73 } 74 75 static inline DSoundRenderImpl *impl_from_IReferenceClock(IReferenceClock *iface) 76 { 77 return CONTAINING_RECORD(iface, DSoundRenderImpl, IReferenceClock_iface); 78 } 79 80 static inline DSoundRenderImpl *impl_from_IAMDirectSound(IAMDirectSound *iface) 81 { 82 return CONTAINING_RECORD(iface, DSoundRenderImpl, IAMDirectSound_iface); 83 } 84 85 static inline DSoundRenderImpl *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface) 86 { 87 return CONTAINING_RECORD(iface, DSoundRenderImpl, IAMFilterMiscFlags_iface); 88 } 89 90 static REFERENCE_TIME time_from_pos(DSoundRenderImpl *This, DWORD pos) { 91 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat; 92 REFERENCE_TIME ret = 10000000; 93 ret = ret * pos / wfx->nAvgBytesPerSec; 94 return ret; 95 } 96 97 static DWORD pos_from_time(DSoundRenderImpl *This, REFERENCE_TIME time) { 98 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat; 99 REFERENCE_TIME ret = time; 100 ret *= wfx->nAvgBytesPerSec; 101 ret /= 10000000; 102 ret -= ret % wfx->nBlockAlign; 103 return ret; 104 } 105 106 static void DSoundRender_UpdatePositions(DSoundRenderImpl *This, DWORD *seqwritepos, DWORD *minwritepos) { 107 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat; 108 BYTE *buf1, *buf2; 109 DWORD size1, size2, playpos, writepos, old_writepos, old_playpos, adv; 110 BOOL writepos_set = This->writepos < This->buf_size; 111 112 /* Update position and zero */ 113 old_writepos = This->writepos; 114 old_playpos = This->last_playpos; 115 if (old_writepos <= old_playpos) 116 old_writepos += This->buf_size; 117 118 IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, &playpos, &writepos); 119 if (old_playpos > playpos) { 120 adv = This->buf_size + playpos - old_playpos; 121 This->play_time += time_from_pos(This, This->buf_size); 122 } else 123 adv = playpos - old_playpos; 124 This->last_playpos = playpos; 125 if (adv) { 126 TRACE("Moving from %u to %u: clearing %u bytes\n", old_playpos, playpos, adv); 127 IDirectSoundBuffer_Lock(This->dsbuffer, old_playpos, adv, (void**)&buf1, &size1, (void**)&buf2, &size2, 0); 128 memset(buf1, wfx->wBitsPerSample == 8 ? 128 : 0, size1); 129 memset(buf2, wfx->wBitsPerSample == 8 ? 128 : 0, size2); 130 IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2); 131 } 132 *minwritepos = writepos; 133 if (!writepos_set || old_writepos < writepos) { 134 if (writepos_set) { 135 This->writepos = This->buf_size; 136 FIXME("Underrun of data occurred!\n"); 137 } 138 *seqwritepos = writepos; 139 } else 140 *seqwritepos = This->writepos; 141 } 142 143 static HRESULT DSoundRender_GetWritePos(DSoundRenderImpl *This, DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip) 144 { 145 WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat; 146 DWORD writepos, min_writepos, playpos; 147 REFERENCE_TIME max_lag = 50 * 10000; 148 REFERENCE_TIME min_lag = 25 * 10000; 149 REFERENCE_TIME cur, writepos_t, delta_t; 150 151 DSoundRender_UpdatePositions(This, &writepos, &min_writepos); 152 playpos = This->last_playpos; 153 if (This->renderer.filter.pClock == &This->IReferenceClock_iface) { 154 max_lag = min_lag; 155 cur = This->play_time + time_from_pos(This, playpos); 156 cur -= This->renderer.filter.rtStreamStart; 157 } else if (This->renderer.filter.pClock) { 158 IReferenceClock_GetTime(This->renderer.filter.pClock, &cur); 159 cur -= This->renderer.filter.rtStreamStart; 160 } else 161 write_at = -1; 162 163 if (writepos == min_writepos) 164 max_lag = 0; 165 166 *skip = 0; 167 if (write_at < 0) { 168 *ret_writepos = writepos; 169 goto end; 170 } 171 172 if (writepos >= playpos) 173 writepos_t = cur + time_from_pos(This, writepos - playpos); 174 else 175 writepos_t = cur + time_from_pos(This, This->buf_size + writepos - playpos); 176 177 /* write_at: Starting time of sample */ 178 /* cur: current time of play position */ 179 /* writepos_t: current time of our pointer play position */ 180 delta_t = write_at - writepos_t; 181 if (delta_t >= -max_lag && delta_t <= max_lag) { 182 TRACE("Continuing from old position\n"); 183 *ret_writepos = writepos; 184 } else if (delta_t < 0) { 185 REFERENCE_TIME past, min_writepos_t; 186 WARN("Delta too big %i/%i, overwriting old data or even skipping\n", (int)delta_t / 10000, (int)max_lag / 10000); 187 if (min_writepos >= playpos) 188 min_writepos_t = cur + time_from_pos(This, min_writepos - playpos); 189 else 190 min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos); 191 past = min_writepos_t - write_at; 192 if (past >= 0) { 193 DWORD skipbytes = pos_from_time(This, past); 194 WARN("Skipping %u bytes\n", skipbytes); 195 *skip = skipbytes; 196 *ret_writepos = min_writepos; 197 } else { 198 DWORD aheadbytes = pos_from_time(This, -past); 199 WARN("Advancing %u bytes\n", aheadbytes); 200 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size; 201 } 202 } else /* delta_t > 0 */ { 203 DWORD aheadbytes; 204 WARN("Delta too big %i/%i, too far ahead\n", (int)delta_t / 10000, (int)max_lag / 10000); 205 aheadbytes = pos_from_time(This, delta_t); 206 WARN("Advancing %u bytes\n", aheadbytes); 207 if (delta_t >= DSoundRenderer_Max_Fill) 208 return S_FALSE; 209 *ret_writepos = (min_writepos + aheadbytes) % This->buf_size; 210 } 211 end: 212 if (playpos > *ret_writepos) 213 *pfree = playpos - *ret_writepos; 214 else if (playpos == *ret_writepos) 215 *pfree = This->buf_size - wfx->nBlockAlign; 216 else 217 *pfree = This->buf_size + playpos - *ret_writepos; 218 if (time_from_pos(This, This->buf_size - *pfree) >= DSoundRenderer_Max_Fill) { 219 TRACE("Blocked: too full %i / %i\n", (int)(time_from_pos(This, This->buf_size - *pfree)/10000), (int)(DSoundRenderer_Max_Fill / 10000)); 220 return S_FALSE; 221 } 222 return S_OK; 223 } 224 225 static HRESULT DSoundRender_HandleEndOfStream(DSoundRenderImpl *This) 226 { 227 while (This->renderer.filter.state == State_Running) 228 { 229 DWORD pos1, pos2; 230 DSoundRender_UpdatePositions(This, &pos1, &pos2); 231 if (pos1 == pos2) 232 break; 233 234 This->in_loop = 1; 235 LeaveCriticalSection(&This->renderer.filter.csFilter); 236 LeaveCriticalSection(&This->renderer.csRenderLock); 237 WaitForSingleObject(This->blocked, 10); 238 EnterCriticalSection(&This->renderer.csRenderLock); 239 EnterCriticalSection(&This->renderer.filter.csFilter); 240 This->in_loop = 0; 241 } 242 243 return S_OK; 244 } 245 246 static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* This, REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size) 247 { 248 HRESULT hr; 249 250 while (size && This->renderer.filter.state != State_Stopped) { 251 DWORD writepos, skip = 0, free, size1, size2, ret; 252 BYTE *buf1, *buf2; 253 254 if (This->renderer.filter.state == State_Running) 255 hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip); 256 else 257 hr = S_FALSE; 258 259 if (hr != S_OK) { 260 This->in_loop = 1; 261 LeaveCriticalSection(&This->renderer.csRenderLock); 262 ret = WaitForSingleObject(This->blocked, 10); 263 EnterCriticalSection(&This->renderer.csRenderLock); 264 This->in_loop = 0; 265 if (This->renderer.pInputPin->flushing || 266 This->renderer.filter.state == State_Stopped) { 267 return This->renderer.filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE; 268 } 269 if (ret != WAIT_TIMEOUT) 270 ERR("%x\n", ret); 271 continue; 272 } 273 tStart = -1; 274 275 if (skip) 276 FIXME("Sample dropped %u of %u bytes\n", skip, size); 277 if (skip >= size) 278 return S_OK; 279 data += skip; 280 size -= skip; 281 282 hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0); 283 if (hr != DS_OK) { 284 ERR("Unable to lock sound buffer! (%x)\n", hr); 285 break; 286 } 287 memcpy(buf1, data, size1); 288 if (size2) 289 memcpy(buf2, data+size1, size2); 290 IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2); 291 This->writepos = (writepos + size1 + size2) % This->buf_size; 292 TRACE("Wrote %u bytes at %u, next at %u - (%u/%u)\n", size1+size2, writepos, This->writepos, free, size); 293 data += size1 + size2; 294 size -= size1 + size2; 295 } 296 return S_OK; 297 } 298 299 static HRESULT WINAPI DSoundRender_ShouldDrawSampleNow(BaseRenderer *This, IMediaSample *pMediaSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime) 300 { 301 /* We time ourselves do not use the base renderers timing */ 302 return S_OK; 303 } 304 305 306 static HRESULT WINAPI DSoundRender_PrepareReceive(BaseRenderer *iface, IMediaSample *pSample) 307 { 308 DSoundRenderImpl *This = impl_from_BaseRenderer(iface); 309 HRESULT hr; 310 AM_MEDIA_TYPE *amt; 311 312 if (IMediaSample_GetMediaType(pSample, &amt) == S_OK) 313 { 314 AM_MEDIA_TYPE *orig = &This->renderer.pInputPin->pin.mtCurrent; 315 WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat; 316 WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat; 317 318 if (origfmt->wFormatTag == newfmt->wFormatTag && 319 origfmt->nChannels == newfmt->nChannels && 320 origfmt->nBlockAlign == newfmt->nBlockAlign && 321 origfmt->wBitsPerSample == newfmt->wBitsPerSample && 322 origfmt->cbSize == newfmt->cbSize) 323 { 324 if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec) 325 { 326 hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer, 327 newfmt->nSamplesPerSec); 328 if (FAILED(hr)) 329 return VFW_E_TYPE_NOT_ACCEPTED; 330 FreeMediaType(orig); 331 CopyMediaType(orig, amt); 332 IMediaSample_SetMediaType(pSample, NULL); 333 } 334 } 335 else 336 return VFW_E_TYPE_NOT_ACCEPTED; 337 } 338 return S_OK; 339 } 340 341 static HRESULT WINAPI DSoundRender_DoRenderSample(BaseRenderer *iface, IMediaSample * pSample) 342 { 343 DSoundRenderImpl *This = impl_from_BaseRenderer(iface); 344 LPBYTE pbSrcStream = NULL; 345 LONG cbSrcStream = 0; 346 REFERENCE_TIME tStart, tStop; 347 HRESULT hr; 348 349 TRACE("%p %p\n", iface, pSample); 350 351 /* Slightly incorrect, Pause completes when a frame is received so we should signal 352 * pause completion here, but for sound playing a single frame doesn't make sense 353 */ 354 355 hr = IMediaSample_GetPointer(pSample, &pbSrcStream); 356 if (FAILED(hr)) 357 { 358 ERR("Cannot get pointer to sample data (%x)\n", hr); 359 return hr; 360 } 361 362 hr = IMediaSample_GetTime(pSample, &tStart, &tStop); 363 if (FAILED(hr)) { 364 ERR("Cannot get sample time (%x)\n", hr); 365 tStart = tStop = -1; 366 } 367 368 IMediaSample_IsDiscontinuity(pSample); 369 370 if (IMediaSample_IsPreroll(pSample) == S_OK) 371 { 372 TRACE("Preroll!\n"); 373 return S_OK; 374 } 375 376 cbSrcStream = IMediaSample_GetActualDataLength(pSample); 377 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream); 378 379 hr = DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream); 380 if (This->renderer.filter.state == State_Running && This->renderer.filter.pClock && tStart >= 0) { 381 REFERENCE_TIME jitter, now = 0; 382 Quality q; 383 IReferenceClock_GetTime(This->renderer.filter.pClock, &now); 384 jitter = now - This->renderer.filter.rtStreamStart - tStart; 385 if (jitter <= -DSoundRenderer_Max_Fill) 386 jitter += DSoundRenderer_Max_Fill; 387 else if (jitter < 0) 388 jitter = 0; 389 q.Type = (jitter > 0 ? Famine : Flood); 390 q.Proportion = 1000; 391 q.Late = jitter; 392 q.TimeStamp = tStart; 393 IQualityControl_Notify((IQualityControl *)This->renderer.qcimpl, (IBaseFilter*)This, q); 394 } 395 return hr; 396 } 397 398 static HRESULT WINAPI DSoundRender_CheckMediaType(BaseRenderer *iface, const AM_MEDIA_TYPE * pmt) 399 { 400 WAVEFORMATEX* format; 401 402 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio)) 403 return S_FALSE; 404 405 format = (WAVEFORMATEX*)pmt->pbFormat; 406 TRACE("Format = %p\n", format); 407 TRACE("wFormatTag = %x %x\n", format->wFormatTag, WAVE_FORMAT_PCM); 408 TRACE("nChannels = %d\n", format->nChannels); 409 TRACE("nSamplesPerSec = %d\n", format->nSamplesPerSec); 410 TRACE("nAvgBytesPerSec = %d\n", format->nAvgBytesPerSec); 411 TRACE("nBlockAlign = %d\n", format->nBlockAlign); 412 TRACE("wBitsPerSample = %d\n", format->wBitsPerSample); 413 414 if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM)) 415 return S_FALSE; 416 417 return S_OK; 418 } 419 420 static VOID WINAPI DSoundRender_OnStopStreaming(BaseRenderer * iface) 421 { 422 DSoundRenderImpl *This = impl_from_BaseRenderer(iface); 423 424 TRACE("(%p/%p)->()\n", This, iface); 425 426 IDirectSoundBuffer_Stop(This->dsbuffer); 427 This->writepos = This->buf_size; 428 SetEvent(This->blocked); 429 } 430 431 static VOID WINAPI DSoundRender_OnStartStreaming(BaseRenderer * iface) 432 { 433 DSoundRenderImpl *This = impl_from_BaseRenderer(iface); 434 435 TRACE("(%p)\n", This); 436 437 if (This->renderer.pInputPin->pin.pConnectedTo) 438 { 439 if (This->renderer.filter.state == State_Paused) 440 { 441 /* Unblock our thread, state changing from paused to running doesn't need a reset for state change */ 442 SetEvent(This->blocked); 443 } 444 IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING); 445 ResetEvent(This->blocked); 446 } 447 } 448 449 static HRESULT WINAPI DSoundRender_CompleteConnect(BaseRenderer * iface, IPin * pReceivePin) 450 { 451 DSoundRenderImpl *This = impl_from_BaseRenderer(iface); 452 const AM_MEDIA_TYPE * pmt = &This->renderer.pInputPin->pin.mtCurrent; 453 HRESULT hr = S_OK; 454 WAVEFORMATEX *format; 455 DSBUFFERDESC buf_desc; 456 457 TRACE("(%p)->(%p)\n", This, pReceivePin); 458 dump_AM_MEDIA_TYPE(pmt); 459 460 TRACE("MajorType %s\n", debugstr_guid(&pmt->majortype)); 461 TRACE("SubType %s\n", debugstr_guid(&pmt->subtype)); 462 TRACE("Format %s\n", debugstr_guid(&pmt->formattype)); 463 TRACE("Size %d\n", pmt->cbFormat); 464 465 format = (WAVEFORMATEX*)pmt->pbFormat; 466 467 This->buf_size = format->nAvgBytesPerSec; 468 469 memset(&buf_desc,0,sizeof(DSBUFFERDESC)); 470 buf_desc.dwSize = sizeof(DSBUFFERDESC); 471 buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN | 472 DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS | 473 DSBCAPS_GETCURRENTPOSITION2; 474 buf_desc.dwBufferBytes = This->buf_size; 475 buf_desc.lpwfxFormat = format; 476 hr = IDirectSound_CreateSoundBuffer(This->dsound, &buf_desc, &This->dsbuffer, NULL); 477 This->writepos = This->buf_size; 478 if (FAILED(hr)) 479 ERR("Can't create sound buffer (%x)\n", hr); 480 481 if (SUCCEEDED(hr)) 482 { 483 hr = IDirectSoundBuffer_SetVolume(This->dsbuffer, This->volume); 484 if (FAILED(hr)) 485 ERR("Can't set volume to %d (%x)\n", This->volume, hr); 486 487 hr = IDirectSoundBuffer_SetPan(This->dsbuffer, This->pan); 488 if (FAILED(hr)) 489 ERR("Can't set pan to %d (%x)\n", This->pan, hr); 490 hr = S_OK; 491 } 492 493 if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED) 494 { 495 if (This->dsbuffer) 496 IDirectSoundBuffer_Release(This->dsbuffer); 497 This->dsbuffer = NULL; 498 } 499 500 return hr; 501 } 502 503 static HRESULT WINAPI DSoundRender_BreakConnect(BaseRenderer* iface) 504 { 505 DSoundRenderImpl *This = impl_from_BaseRenderer(iface); 506 507 TRACE("(%p)->()\n", iface); 508 509 if (This->threadid) { 510 PostThreadMessageW(This->threadid, WM_APP, 0, 0); 511 LeaveCriticalSection(This->renderer.pInputPin->pin.pCritSec); 512 WaitForSingleObject(This->advisethread, INFINITE); 513 EnterCriticalSection(This->renderer.pInputPin->pin.pCritSec); 514 CloseHandle(This->advisethread); 515 } 516 if (This->dsbuffer) 517 IDirectSoundBuffer_Release(This->dsbuffer); 518 This->dsbuffer = NULL; 519 520 return S_OK; 521 } 522 523 static HRESULT WINAPI DSoundRender_EndOfStream(BaseRenderer* iface) 524 { 525 DSoundRenderImpl *This = impl_from_BaseRenderer(iface); 526 HRESULT hr; 527 528 TRACE("(%p)->()\n",iface); 529 530 hr = BaseRendererImpl_EndOfStream(iface); 531 if (hr != S_OK) 532 { 533 ERR("%08x\n", hr); 534 return hr; 535 } 536 537 hr = DSoundRender_HandleEndOfStream(This); 538 539 return hr; 540 } 541 542 static HRESULT WINAPI DSoundRender_BeginFlush(BaseRenderer* iface) 543 { 544 DSoundRenderImpl *This = impl_from_BaseRenderer(iface); 545 546 TRACE("\n"); 547 BaseRendererImpl_BeginFlush(iface); 548 SetEvent(This->blocked); 549 550 return S_OK; 551 } 552 553 static HRESULT WINAPI DSoundRender_EndFlush(BaseRenderer* iface) 554 { 555 DSoundRenderImpl *This = impl_from_BaseRenderer(iface); 556 557 TRACE("\n"); 558 559 BaseRendererImpl_EndFlush(iface); 560 if (This->renderer.filter.state != State_Stopped) 561 ResetEvent(This->blocked); 562 563 if (This->dsbuffer) 564 { 565 LPBYTE buffer; 566 DWORD size; 567 568 /* Force a reset */ 569 IDirectSoundBuffer_Lock(This->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER); 570 memset(buffer, 0, size); 571 IDirectSoundBuffer_Unlock(This->dsbuffer, buffer, size, NULL, 0); 572 This->writepos = This->buf_size; 573 } 574 575 return S_OK; 576 } 577 578 static const BaseRendererFuncTable BaseFuncTable = { 579 DSoundRender_CheckMediaType, 580 DSoundRender_DoRenderSample, 581 /**/ 582 NULL, 583 NULL, 584 NULL, 585 DSoundRender_OnStartStreaming, 586 DSoundRender_OnStopStreaming, 587 NULL, 588 NULL, 589 NULL, 590 DSoundRender_ShouldDrawSampleNow, 591 DSoundRender_PrepareReceive, 592 /**/ 593 DSoundRender_CompleteConnect, 594 DSoundRender_BreakConnect, 595 DSoundRender_EndOfStream, 596 DSoundRender_BeginFlush, 597 DSoundRender_EndFlush, 598 }; 599 600 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv) 601 { 602 HRESULT hr; 603 DSoundRenderImpl * pDSoundRender; 604 605 TRACE("(%p, %p)\n", pUnkOuter, ppv); 606 607 *ppv = NULL; 608 609 if (pUnkOuter) 610 return CLASS_E_NOAGGREGATION; 611 612 pDSoundRender = CoTaskMemAlloc(sizeof(DSoundRenderImpl)); 613 if (!pDSoundRender) 614 return E_OUTOFMEMORY; 615 ZeroMemory(pDSoundRender, sizeof(DSoundRenderImpl)); 616 617 hr = BaseRenderer_Init(&pDSoundRender->renderer, &DSoundRender_Vtbl, (IUnknown*)pDSoundRender, &CLSID_DSoundRender, (DWORD_PTR)(__FILE__ ": DSoundRenderImpl.csFilter"), &BaseFuncTable); 618 619 BasicAudio_Init(&pDSoundRender->basicAudio,&IBasicAudio_Vtbl); 620 pDSoundRender->IReferenceClock_iface.lpVtbl = &IReferenceClock_Vtbl; 621 pDSoundRender->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl; 622 pDSoundRender->IAMFilterMiscFlags_iface.lpVtbl = &IAMFilterMiscFlags_Vtbl; 623 624 if (SUCCEEDED(hr)) 625 { 626 hr = DirectSoundCreate8(NULL, &pDSoundRender->dsound, NULL); 627 if (FAILED(hr)) 628 ERR("Cannot create Direct Sound object (%x)\n", hr); 629 else 630 hr = IDirectSound_SetCooperativeLevel(pDSoundRender->dsound, GetDesktopWindow(), DSSCL_PRIORITY); 631 if (SUCCEEDED(hr)) { 632 IDirectSoundBuffer *buf; 633 DSBUFFERDESC buf_desc; 634 memset(&buf_desc,0,sizeof(DSBUFFERDESC)); 635 buf_desc.dwSize = sizeof(DSBUFFERDESC); 636 buf_desc.dwFlags = DSBCAPS_PRIMARYBUFFER; 637 hr = IDirectSound_CreateSoundBuffer(pDSoundRender->dsound, &buf_desc, &buf, NULL); 638 if (SUCCEEDED(hr)) { 639 IDirectSoundBuffer_Play(buf, 0, 0, DSBPLAY_LOOPING); 640 IDirectSoundBuffer_Release(buf); 641 } 642 hr = S_OK; 643 } 644 } 645 646 if (SUCCEEDED(hr)) 647 { 648 pDSoundRender->blocked = CreateEventW(NULL, TRUE, TRUE, NULL); 649 650 if (!pDSoundRender->blocked || FAILED(hr)) 651 { 652 IBaseFilter_Release(&pDSoundRender->renderer.filter.IBaseFilter_iface); 653 return HRESULT_FROM_WIN32(GetLastError()); 654 } 655 656 *ppv = pDSoundRender; 657 } 658 else 659 { 660 BaseRendererImpl_Release(&pDSoundRender->renderer.filter.IBaseFilter_iface); 661 CoTaskMemFree(pDSoundRender); 662 } 663 664 return hr; 665 } 666 667 static HRESULT WINAPI DSoundRender_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv) 668 { 669 DSoundRenderImpl *This = impl_from_IBaseFilter(iface); 670 TRACE("(%p, %p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv); 671 672 *ppv = NULL; 673 674 if (IsEqualIID(riid, &IID_IBasicAudio)) 675 *ppv = &This->basicAudio.IBasicAudio_iface; 676 else if (IsEqualIID(riid, &IID_IReferenceClock)) 677 *ppv = &This->IReferenceClock_iface; 678 else if (IsEqualIID(riid, &IID_IAMDirectSound)) 679 *ppv = &This->IAMDirectSound_iface; 680 else if (IsEqualIID(riid, &IID_IAMFilterMiscFlags)) 681 *ppv = &This->IAMFilterMiscFlags_iface; 682 else 683 { 684 HRESULT hr; 685 hr = BaseRendererImpl_QueryInterface(iface, riid, ppv); 686 if (SUCCEEDED(hr)) 687 return hr; 688 } 689 690 if (*ppv) 691 { 692 IUnknown_AddRef((IUnknown *)(*ppv)); 693 return S_OK; 694 } 695 696 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow)) 697 FIXME("No interface for %s!\n", qzdebugstr_guid(riid)); 698 699 return E_NOINTERFACE; 700 } 701 702 static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface) 703 { 704 DSoundRenderImpl *This = impl_from_IBaseFilter(iface); 705 ULONG refCount = BaseRendererImpl_Release(iface); 706 707 TRACE("(%p)->() Release from %d\n", This, refCount + 1); 708 709 if (!refCount) 710 { 711 if (This->threadid) { 712 PostThreadMessageW(This->threadid, WM_APP, 0, 0); 713 WaitForSingleObject(This->advisethread, INFINITE); 714 CloseHandle(This->advisethread); 715 } 716 717 if (This->dsbuffer) 718 IDirectSoundBuffer_Release(This->dsbuffer); 719 This->dsbuffer = NULL; 720 if (This->dsound) 721 IDirectSound_Release(This->dsound); 722 This->dsound = NULL; 723 724 BasicAudio_Destroy(&This->basicAudio); 725 CloseHandle(This->blocked); 726 727 TRACE("Destroying Audio Renderer\n"); 728 CoTaskMemFree(This); 729 730 return 0; 731 } 732 else 733 return refCount; 734 } 735 736 static HRESULT WINAPI DSoundRender_Pause(IBaseFilter * iface) 737 { 738 HRESULT hr = S_OK; 739 DSoundRenderImpl *This = (DSoundRenderImpl *)iface; 740 741 TRACE("(%p/%p)->()\n", This, iface); 742 743 EnterCriticalSection(&This->renderer.csRenderLock); 744 if (This->renderer.filter.state != State_Paused) 745 { 746 if (This->renderer.filter.state == State_Stopped) 747 { 748 if (This->renderer.pInputPin->pin.pConnectedTo) 749 ResetEvent(This->renderer.evComplete); 750 This->renderer.pInputPin->end_of_stream = 0; 751 } 752 753 hr = IDirectSoundBuffer_Stop(This->dsbuffer); 754 if (SUCCEEDED(hr)) 755 This->renderer.filter.state = State_Paused; 756 757 ResetEvent(This->blocked); 758 ResetEvent(This->renderer.RenderEvent); 759 } 760 ResetEvent(This->renderer.ThreadSignal); 761 LeaveCriticalSection(&This->renderer.csRenderLock); 762 763 return hr; 764 } 765 766 static const IBaseFilterVtbl DSoundRender_Vtbl = 767 { 768 DSoundRender_QueryInterface, 769 BaseFilterImpl_AddRef, 770 DSoundRender_Release, 771 BaseFilterImpl_GetClassID, 772 BaseRendererImpl_Stop, 773 DSoundRender_Pause, 774 BaseRendererImpl_Run, 775 BaseRendererImpl_GetState, 776 BaseRendererImpl_SetSyncSource, 777 BaseFilterImpl_GetSyncSource, 778 BaseFilterImpl_EnumPins, 779 BaseRendererImpl_FindPin, 780 BaseFilterImpl_QueryFilterInfo, 781 BaseFilterImpl_JoinFilterGraph, 782 BaseFilterImpl_QueryVendorInfo 783 }; 784 785 /*** IUnknown methods ***/ 786 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface, 787 REFIID riid, 788 LPVOID*ppvObj) { 789 DSoundRenderImpl *This = impl_from_IBasicAudio(iface); 790 791 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj); 792 793 return DSoundRender_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppvObj); 794 } 795 796 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) { 797 DSoundRenderImpl *This = impl_from_IBasicAudio(iface); 798 799 TRACE("(%p/%p)->()\n", This, iface); 800 801 return BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface); 802 } 803 804 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) { 805 DSoundRenderImpl *This = impl_from_IBasicAudio(iface); 806 807 TRACE("(%p/%p)->()\n", This, iface); 808 809 return DSoundRender_Release(&This->renderer.filter.IBaseFilter_iface); 810 } 811 812 /*** IBasicAudio methods ***/ 813 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface, 814 LONG lVolume) { 815 DSoundRenderImpl *This = impl_from_IBasicAudio(iface); 816 817 TRACE("(%p/%p)->(%d)\n", This, iface, lVolume); 818 819 if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN) 820 return E_INVALIDARG; 821 822 if (This->dsbuffer) { 823 if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume))) 824 return E_FAIL; 825 } 826 827 This->volume = lVolume; 828 return S_OK; 829 } 830 831 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface, 832 LONG *plVolume) { 833 DSoundRenderImpl *This = impl_from_IBasicAudio(iface); 834 835 TRACE("(%p/%p)->(%p)\n", This, iface, plVolume); 836 837 if (!plVolume) 838 return E_POINTER; 839 840 *plVolume = This->volume; 841 return S_OK; 842 } 843 844 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface, 845 LONG lBalance) { 846 DSoundRenderImpl *This = impl_from_IBasicAudio(iface); 847 848 TRACE("(%p/%p)->(%d)\n", This, iface, lBalance); 849 850 if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT) 851 return E_INVALIDARG; 852 853 if (This->dsbuffer) { 854 if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance))) 855 return E_FAIL; 856 } 857 858 This->pan = lBalance; 859 return S_OK; 860 } 861 862 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface, 863 LONG *plBalance) { 864 DSoundRenderImpl *This = impl_from_IBasicAudio(iface); 865 866 TRACE("(%p/%p)->(%p)\n", This, iface, plBalance); 867 868 if (!plBalance) 869 return E_POINTER; 870 871 *plBalance = This->pan; 872 return S_OK; 873 } 874 875 static const IBasicAudioVtbl IBasicAudio_Vtbl = 876 { 877 Basicaudio_QueryInterface, 878 Basicaudio_AddRef, 879 Basicaudio_Release, 880 BasicAudioImpl_GetTypeInfoCount, 881 BasicAudioImpl_GetTypeInfo, 882 BasicAudioImpl_GetIDsOfNames, 883 BasicAudioImpl_Invoke, 884 Basicaudio_put_Volume, 885 Basicaudio_get_Volume, 886 Basicaudio_put_Balance, 887 Basicaudio_get_Balance 888 }; 889 890 struct dsoundrender_timer { 891 struct dsoundrender_timer *next; 892 REFERENCE_TIME start; 893 REFERENCE_TIME periodicity; 894 HANDLE handle; 895 DWORD cookie; 896 }; 897 static LONG cookie_counter = 1; 898 899 static DWORD WINAPI DSoundAdviseThread(LPVOID lpParam) { 900 DSoundRenderImpl *This = lpParam; 901 struct dsoundrender_timer head = {NULL}; 902 MSG msg; 903 904 TRACE("(%p): Main Loop\n", This); 905 906 PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); 907 SetEvent(This->thread_wait); 908 909 while (1) 910 { 911 HRESULT hr; 912 REFERENCE_TIME curtime = 0; 913 BOOL ret; 914 struct dsoundrender_timer *prev = &head, *cur; 915 916 hr = IReferenceClock_GetTime(&This->IReferenceClock_iface, &curtime); 917 if (SUCCEEDED(hr)) { 918 TRACE("Time: %s\n", wine_dbgstr_longlong(curtime)); 919 while (prev->next) { 920 cur = prev->next; 921 if (cur->start > curtime) { 922 TRACE("Skipping %p\n", cur); 923 prev = cur; 924 } else if (cur->periodicity) { 925 while (cur->start <= curtime) { 926 cur->start += cur->periodicity; 927 ReleaseSemaphore(cur->handle, 1, NULL); 928 } 929 prev = cur; 930 } else { 931 struct dsoundrender_timer *next = cur->next; 932 TRACE("Firing %p %s < %s\n", cur, wine_dbgstr_longlong(cur->start), wine_dbgstr_longlong(curtime)); 933 SetEvent(cur->handle); 934 HeapFree(GetProcessHeap(), 0, cur); 935 prev->next = next; 936 } 937 } 938 } 939 if (!head.next) 940 ret = GetMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4); 941 else 942 ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE); 943 while (ret) { 944 switch (LOWORD(msg.message) - WM_APP) { 945 case 0: TRACE("Exiting\n"); return 0; 946 case 1: 947 case 2: { 948 struct dsoundrender_timer *t = (struct dsoundrender_timer *)msg.wParam; 949 if (LOWORD(msg.message) - WM_APP == 1) 950 TRACE("Adding one-shot timer %p\n", t); 951 else 952 TRACE("Adding periodic timer %p\n", t); 953 t->next = head.next; 954 head.next = t; 955 break; 956 } 957 case 3: 958 prev = &head; 959 while (prev->next) { 960 cur = prev->next; 961 if (cur->cookie == msg.wParam) { 962 struct dsoundrender_timer *next = cur->next; 963 HeapFree(GetProcessHeap(), 0, cur); 964 prev->next = next; 965 break; 966 } 967 prev = cur; 968 } 969 break; 970 } 971 ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE); 972 } 973 MsgWaitForMultipleObjects(0, NULL, 5, QS_POSTMESSAGE, 0); 974 } 975 return 0; 976 } 977 978 /*** IUnknown methods ***/ 979 static HRESULT WINAPI ReferenceClock_QueryInterface(IReferenceClock *iface, 980 REFIID riid, 981 LPVOID*ppvObj) 982 { 983 DSoundRenderImpl *This = impl_from_IReferenceClock(iface); 984 985 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj); 986 987 return DSoundRender_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppvObj); 988 } 989 990 static ULONG WINAPI ReferenceClock_AddRef(IReferenceClock *iface) 991 { 992 DSoundRenderImpl *This = impl_from_IReferenceClock(iface); 993 994 TRACE("(%p/%p)->()\n", This, iface); 995 996 return BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface); 997 } 998 999 static ULONG WINAPI ReferenceClock_Release(IReferenceClock *iface) 1000 { 1001 DSoundRenderImpl *This = impl_from_IReferenceClock(iface); 1002 1003 TRACE("(%p/%p)->()\n", This, iface); 1004 1005 return DSoundRender_Release(&This->renderer.filter.IBaseFilter_iface); 1006 } 1007 1008 /*** IReferenceClock methods ***/ 1009 static HRESULT WINAPI ReferenceClock_GetTime(IReferenceClock *iface, 1010 REFERENCE_TIME *pTime) 1011 { 1012 DSoundRenderImpl *This = impl_from_IReferenceClock(iface); 1013 HRESULT hr = E_FAIL; 1014 1015 TRACE("(%p/%p)->(%p)\n", This, iface, pTime); 1016 if (!pTime) 1017 return E_POINTER; 1018 1019 if (This->dsbuffer) { 1020 DWORD writepos1, writepos2; 1021 EnterCriticalSection(&This->renderer.filter.csFilter); 1022 DSoundRender_UpdatePositions(This, &writepos1, &writepos2); 1023 if (This->renderer.pInputPin && This->renderer.pInputPin->pin.mtCurrent.pbFormat) 1024 { 1025 *pTime = This->play_time + time_from_pos(This, This->last_playpos); 1026 hr = S_OK; 1027 } 1028 else 1029 { 1030 ERR("pInputPin Disconnected\n"); 1031 hr = E_FAIL; 1032 } 1033 LeaveCriticalSection(&This->renderer.filter.csFilter); 1034 } 1035 if (FAILED(hr)) 1036 WARN("Could not get reference time (%x)!\n", hr); 1037 1038 return hr; 1039 } 1040 1041 static HRESULT WINAPI ReferenceClock_AdviseTime(IReferenceClock *iface, 1042 REFERENCE_TIME rtBaseTime, 1043 REFERENCE_TIME rtStreamTime, 1044 HEVENT hEvent, 1045 DWORD_PTR *pdwAdviseCookie) 1046 { 1047 DSoundRenderImpl *This = impl_from_IReferenceClock(iface); 1048 REFERENCE_TIME when = rtBaseTime + rtStreamTime; 1049 REFERENCE_TIME future; 1050 TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hEvent, pdwAdviseCookie); 1051 1052 if (when <= 0) 1053 return E_INVALIDARG; 1054 1055 if (!pdwAdviseCookie) 1056 return E_POINTER; 1057 1058 EnterCriticalSection(&This->renderer.filter.csFilter); 1059 future = when - This->play_time; 1060 if (!This->threadid && This->dsbuffer) { 1061 This->thread_wait = CreateEventW(0, 0, 0, 0); 1062 This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid); 1063 WaitForSingleObject(This->thread_wait, INFINITE); 1064 CloseHandle(This->thread_wait); 1065 } 1066 LeaveCriticalSection(&This->renderer.filter.csFilter); 1067 /* If it's in the past or the next millisecond, trigger immediately */ 1068 if (future <= 10000) { 1069 SetEvent((HANDLE)hEvent); 1070 *pdwAdviseCookie = 0; 1071 } else { 1072 struct dsoundrender_timer *t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t)); 1073 t->next = NULL; 1074 t->start = when; 1075 t->periodicity = 0; 1076 t->handle = (HANDLE)hEvent; 1077 t->cookie = InterlockedIncrement(&cookie_counter); 1078 PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0); 1079 *pdwAdviseCookie = t->cookie; 1080 } 1081 1082 return S_OK; 1083 } 1084 1085 static HRESULT WINAPI ReferenceClock_AdvisePeriodic(IReferenceClock *iface, 1086 REFERENCE_TIME rtStartTime, 1087 REFERENCE_TIME rtPeriodTime, 1088 HSEMAPHORE hSemaphore, 1089 DWORD_PTR *pdwAdviseCookie) 1090 { 1091 DSoundRenderImpl *This = impl_from_IReferenceClock(iface); 1092 struct dsoundrender_timer *t; 1093 1094 TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtStartTime), wine_dbgstr_longlong(rtPeriodTime), (void*)hSemaphore, pdwAdviseCookie); 1095 1096 if (rtStartTime <= 0 || rtPeriodTime <= 0) 1097 return E_INVALIDARG; 1098 1099 if (!pdwAdviseCookie) 1100 return E_POINTER; 1101 1102 EnterCriticalSection(&This->renderer.filter.csFilter); 1103 if (!This->threadid && This->dsbuffer) { 1104 This->thread_wait = CreateEventW(0, 0, 0, 0); 1105 This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid); 1106 WaitForSingleObject(This->thread_wait, INFINITE); 1107 CloseHandle(This->thread_wait); 1108 } 1109 LeaveCriticalSection(&This->renderer.filter.csFilter); 1110 1111 t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t)); 1112 t->next = NULL; 1113 t->start = rtStartTime; 1114 t->periodicity = rtPeriodTime; 1115 t->handle = (HANDLE)hSemaphore; 1116 t->cookie = InterlockedIncrement(&cookie_counter); 1117 PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0); 1118 *pdwAdviseCookie = t->cookie; 1119 1120 return S_OK; 1121 } 1122 1123 static HRESULT WINAPI ReferenceClock_Unadvise(IReferenceClock *iface, 1124 DWORD_PTR dwAdviseCookie) 1125 { 1126 DSoundRenderImpl *This = impl_from_IReferenceClock(iface); 1127 1128 TRACE("(%p/%p)->(%p)\n", This, iface, (void*)dwAdviseCookie); 1129 if (!This->advisethread || !dwAdviseCookie) 1130 return S_FALSE; 1131 PostThreadMessageW(This->threadid, WM_APP+3, dwAdviseCookie, 0); 1132 return S_OK; 1133 } 1134 1135 static const IReferenceClockVtbl IReferenceClock_Vtbl = 1136 { 1137 ReferenceClock_QueryInterface, 1138 ReferenceClock_AddRef, 1139 ReferenceClock_Release, 1140 ReferenceClock_GetTime, 1141 ReferenceClock_AdviseTime, 1142 ReferenceClock_AdvisePeriodic, 1143 ReferenceClock_Unadvise 1144 }; 1145 1146 /*** IUnknown methods ***/ 1147 static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface, 1148 REFIID riid, 1149 LPVOID*ppvObj) 1150 { 1151 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1152 1153 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj); 1154 1155 return DSoundRender_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppvObj); 1156 } 1157 1158 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface) 1159 { 1160 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1161 1162 TRACE("(%p/%p)->()\n", This, iface); 1163 1164 return BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface); 1165 } 1166 1167 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface) 1168 { 1169 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1170 1171 TRACE("(%p/%p)->()\n", This, iface); 1172 1173 return DSoundRender_Release(&This->renderer.filter.IBaseFilter_iface); 1174 } 1175 1176 /*** IAMDirectSound methods ***/ 1177 static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface, IDirectSound **ds) 1178 { 1179 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1180 1181 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds); 1182 1183 return E_NOTIMPL; 1184 } 1185 1186 static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf) 1187 { 1188 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1189 1190 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf); 1191 1192 return E_NOTIMPL; 1193 } 1194 1195 static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf) 1196 { 1197 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1198 1199 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf); 1200 1201 return E_NOTIMPL; 1202 } 1203 1204 static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds) 1205 { 1206 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1207 1208 FIXME("(%p/%p)->(%p): stub\n", This, iface, ds); 1209 1210 return E_NOTIMPL; 1211 } 1212 1213 static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf) 1214 { 1215 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1216 1217 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf); 1218 1219 return E_NOTIMPL; 1220 } 1221 1222 static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf) 1223 { 1224 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1225 1226 FIXME("(%p/%p)->(%p): stub\n", This, iface, buf); 1227 1228 return E_NOTIMPL; 1229 } 1230 1231 static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgaudible) 1232 { 1233 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1234 1235 FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgaudible); 1236 1237 return E_NOTIMPL; 1238 } 1239 1240 static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND *hwnd, BOOL *bgaudible) 1241 { 1242 DSoundRenderImpl *This = impl_from_IAMDirectSound(iface); 1243 1244 FIXME("(%p/%p)->(%p,%p): stub\n", This, iface, hwnd, bgaudible); 1245 1246 return E_NOTIMPL; 1247 } 1248 1249 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl = 1250 { 1251 AMDirectSound_QueryInterface, 1252 AMDirectSound_AddRef, 1253 AMDirectSound_Release, 1254 AMDirectSound_GetDirectSoundInterface, 1255 AMDirectSound_GetPrimaryBufferInterface, 1256 AMDirectSound_GetSecondaryBufferInterface, 1257 AMDirectSound_ReleaseDirectSoundInterface, 1258 AMDirectSound_ReleasePrimaryBufferInterface, 1259 AMDirectSound_ReleaseSecondaryBufferInterface, 1260 AMDirectSound_SetFocusWindow, 1261 AMDirectSound_GetFocusWindow 1262 }; 1263 1264 static HRESULT WINAPI AMFilterMiscFlags_QueryInterface(IAMFilterMiscFlags *iface, REFIID riid, void **ppv) { 1265 DSoundRenderImpl *This = impl_from_IAMFilterMiscFlags(iface); 1266 return IBaseFilter_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppv); 1267 } 1268 1269 static ULONG WINAPI AMFilterMiscFlags_AddRef(IAMFilterMiscFlags *iface) { 1270 DSoundRenderImpl *This = impl_from_IAMFilterMiscFlags(iface); 1271 return IBaseFilter_AddRef(&This->renderer.filter.IBaseFilter_iface); 1272 } 1273 1274 static ULONG WINAPI AMFilterMiscFlags_Release(IAMFilterMiscFlags *iface) { 1275 DSoundRenderImpl *This = impl_from_IAMFilterMiscFlags(iface); 1276 return IBaseFilter_Release(&This->renderer.filter.IBaseFilter_iface); 1277 } 1278 1279 static ULONG WINAPI AMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags *iface) { 1280 return AM_FILTER_MISC_FLAGS_IS_RENDERER; 1281 } 1282 1283 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl = { 1284 AMFilterMiscFlags_QueryInterface, 1285 AMFilterMiscFlags_AddRef, 1286 AMFilterMiscFlags_Release, 1287 AMFilterMiscFlags_GetMiscFlags 1288 }; 1289