Changeset 88514 in vbox for trunk/src/VBox/Devices/Audio
- Timestamp:
- Apr 14, 2021 8:03:28 PM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 143781
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp
r88452 r88514 48 48 /* 49 49 * IDirectSound* interface uses HRESULT status codes and the driver callbacks use 50 * the IPRT status codes. To minimize HRESULT->IPRTconversion most internal functions50 * the VBox status codes. To minimize HRESULT->VBox conversion most internal functions 51 51 * in the driver return HRESULT and conversion is done in the driver callbacks. 52 52 * 53 53 * Naming convention: 54 * 'dsound*' functions return IPRTstatus code;54 * 'dsound*' functions return VBox status code; 55 55 * 'directSound*' - return HRESULT. 56 56 */ … … 59 59 * Optional release logging, which a user can turn on with the 60 60 * 'VBoxManage debugvm' command. 61 * Debug logging still uses the common Log* macros from IPRT.61 * Debug logging still uses the common Log* macros from VBox. 62 62 * Messages which always should go to the release log use LogRel. 63 63 */ … … 260 260 } 261 261 262 static int dsoundWaveFmtFromCfg(PPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt)263 {264 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);265 AssertPtrReturn(pFmt, VERR_INVALID_POINTER);266 267 RT_BZERO(pFmt, sizeof(WAVEFORMATEX));268 269 pFmt->wFormatTag = WAVE_FORMAT_PCM;270 pFmt->nChannels = PDMAudioPropsChannels(&pCfg->Props);271 pFmt->wBitsPerSample = PDMAudioPropsSampleBits(&pCfg->Props);272 pFmt->nSamplesPerSec = PDMAudioPropsHz(&pCfg->Props);273 pFmt->nBlockAlign = PDMAudioPropsFrameSize(&pCfg->Props);274 pFmt->nAvgBytesPerSec = PDMAudioPropsFramesToBytes(&pCfg->Props, PDMAudioPropsHz(&pCfg->Props));275 pFmt->cbSize = 0; /* No extra data specified. */276 277 return VINF_SUCCESS;278 }279 280 /**281 * Retrieves the number of free bytes available for writing to a DirectSound output stream.282 *283 * @return VBox status code. VERR_NOT_AVAILABLE if unable to determine or the284 * buffer was not recoverable.285 * @param pThis Host audio driver instance.286 * @param pStreamDS DirectSound output stream to retrieve number for.287 * @param pdwFree Where to return the free amount on success.288 * @param poffPlayCursor Where to return the play cursor offset.289 */290 static int dsoundGetFreeOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, DWORD *pdwFree, DWORD *poffPlayCursor)291 {292 AssertPtrReturn(pThis, VERR_INVALID_POINTER);293 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);294 AssertPtrReturn(pdwFree, VERR_INVALID_POINTER);295 AssertPtr(poffPlayCursor);296 297 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT); /* Paranoia. */298 299 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB;300 if (!pDSB)301 {302 AssertPtr(pDSB);303 return VERR_INVALID_POINTER;304 }305 306 HRESULT hr = S_OK;307 308 /* Get the current play position which is used for calculating the free space in the buffer. */309 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)310 {311 DWORD offPlayCursor = 0;312 DWORD offWriteCursor = 0;313 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &offPlayCursor, &offWriteCursor);314 if (SUCCEEDED(hr))315 {316 int32_t cbDiff = offWriteCursor - offPlayCursor;317 if (cbDiff < 0)318 cbDiff += pStreamDS->cbBufSize;319 320 int32_t cbFree = offPlayCursor - pStreamDS->Out.offWritePos;321 if (cbFree < 0)322 cbFree += pStreamDS->cbBufSize;323 324 if (cbFree > (int32_t)pStreamDS->cbBufSize - cbDiff)325 {326 /** @todo count/log these. */327 pStreamDS->Out.offWritePos = offWriteCursor;328 cbFree = pStreamDS->cbBufSize - cbDiff;329 }330 331 /* When starting to use a DirectSound buffer, offPlayCursor and offWriteCursor332 * both point at position 0, so we won't be able to detect how many bytes333 * are writable that way.334 *335 * So use our per-stream written indicator to see if we just started a stream. */336 if (pStreamDS->Out.cbWritten == 0)337 cbFree = pStreamDS->cbBufSize;338 339 DSLOGREL(("DSound: offPlayCursor=%RU32, offWriteCursor=%RU32, offWritePos=%RU32 -> cbFree=%RI32\n",340 offPlayCursor, offWriteCursor, pStreamDS->Out.offWritePos, cbFree));341 342 *pdwFree = cbFree;343 *poffPlayCursor = offPlayCursor;344 return VINF_SUCCESS;345 }346 347 if (hr != DSERR_BUFFERLOST) /** @todo MSDN doesn't state this error for GetCurrentPosition(). */348 break;349 350 LogFunc(("Getting playing position failed due to lost buffer, restoring ...\n"));351 352 directSoundPlayRestore(pThis, pDSB);353 }354 355 if (hr != DSERR_BUFFERLOST) /* Avoid log flooding if the error is still there. */356 DSLOGREL(("DSound: Getting current playback position failed with %Rhrc\n", hr));357 358 LogFunc(("Failed with %Rhrc\n", hr));359 360 *poffPlayCursor = pStreamDS->cbBufSize;361 return VERR_NOT_AVAILABLE;362 }363 262 364 263 static char *dsoundGUIDToUtf8StrA(LPCGUID pGUID) … … 380 279 return RTStrDup("{Default device}"); 381 280 } 281 382 282 383 283 static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB) … … 393 293 } 394 294 295 395 296 static HRESULT directSoundPlayUnlock(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB, 396 297 PVOID pv1, PVOID pv2, … … 404 305 } 405 306 406 static HRESULT directSoundCaptureUnlock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB,407 PVOID pv1, PVOID pv2,408 DWORD cb1, DWORD cb2)409 {410 HRESULT hr = IDirectSoundCaptureBuffer8_Unlock(pDSCB, pv1, cb1, pv2, cb2);411 if (FAILED(hr))412 DSLOGREL(("DSound: Unlocking capture buffer failed with %Rhrc\n", hr));413 return hr;414 }415 307 416 308 static HRESULT directSoundPlayLock(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, … … 461 353 } 462 354 355 356 static HRESULT directSoundCaptureUnlock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB, 357 PVOID pv1, PVOID pv2, 358 DWORD cb1, DWORD cb2) 359 { 360 HRESULT hr = IDirectSoundCaptureBuffer8_Unlock(pDSCB, pv1, cb1, pv2, cb2); 361 if (FAILED(hr)) 362 DSLOGREL(("DSound: Unlocking capture buffer failed with %Rhrc\n", hr)); 363 return hr; 364 } 365 366 463 367 static HRESULT directSoundCaptureLock(PDSOUNDSTREAM pStreamDS, 464 368 DWORD dwOffset, DWORD dwBytes, … … 497 401 } 498 402 403 499 404 /* 500 405 * DirectSound playback … … 516 421 } 517 422 } 423 518 424 519 425 /** … … 568 474 } 569 475 570 static HRESULT directSoundPlayClose(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)571 {572 AssertPtrReturn(pThis, E_POINTER);573 AssertPtrReturn(pStreamDS, E_POINTER);574 575 LogFlowFuncEnter();576 577 HRESULT hr = directSoundPlayStop(pThis, pStreamDS, true /* fFlush */);578 if (SUCCEEDED(hr))579 {580 DSLOG(("DSound: Closing playback stream\n"));581 RTCritSectEnter(&pThis->CritSect);582 583 if (pStreamDS->Out.pDSB)584 {585 IDirectSoundBuffer8_Release(pStreamDS->Out.pDSB);586 pStreamDS->Out.pDSB = NULL;587 }588 589 pThis->pDSStrmOut = NULL;590 591 RTCritSectLeave(&pThis->CritSect);592 }593 594 if (FAILED(hr))595 DSLOGREL(("DSound: Stopping playback stream %p failed with %Rhrc\n", pStreamDS, hr));596 597 return hr;598 }599 600 static HRESULT directSoundPlayOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,601 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)602 {603 AssertPtrReturn(pThis, E_POINTER);604 AssertPtrReturn(pStreamDS, E_POINTER);605 AssertPtrReturn(pCfgReq, E_POINTER);606 AssertPtrReturn(pCfgAcq, E_POINTER);607 608 /** @todo r=bird: I cannot see any code populating pCfgAcq... */609 610 LogFlowFuncEnter();611 612 Assert(pStreamDS->Out.pDSB == NULL);613 614 DSLOG(("DSound: Opening playback stream (uHz=%RU32, cChannels=%RU8, cBits=%u, fSigned=%RTbool)\n", pCfgReq->Props.uHz,615 PDMAudioPropsChannels(&pCfgReq->Props), PDMAudioPropsSampleBits(&pCfgReq->Props), pCfgReq->Props.fSigned));616 617 WAVEFORMATEX wfx;618 int rc = dsoundWaveFmtFromCfg(pCfgReq, &wfx);619 if (RT_FAILURE(rc))620 return E_INVALIDARG;621 622 DSLOG(("DSound: Requested playback format:\n"623 " wFormatTag = %RI16\n"624 " nChannels = %RI16\n"625 " nSamplesPerSec = %RU32\n"626 " nAvgBytesPerSec = %RU32\n"627 " nBlockAlign = %RI16\n"628 " wBitsPerSample = %RI16\n"629 " cbSize = %RI16\n",630 wfx.wFormatTag,631 wfx.nChannels,632 wfx.nSamplesPerSec,633 wfx.nAvgBytesPerSec,634 wfx.nBlockAlign,635 wfx.wBitsPerSample,636 wfx.cbSize));637 638 /** @todo r=bird: Why is this called every time? It triggers a device639 * enumeration. Andy claimed on IRC that enumeration was only done once...640 * It's generally a 'ing waste of time here too, as we dont really use any of641 * the information we gather there. */642 dsoundUpdateStatusInternal(pThis);643 644 HRESULT hr = directSoundPlayInterfaceCreate(pThis->Cfg.pGuidPlay, &pThis->pDS);645 if (FAILED(hr))646 return hr;647 648 do /* To use breaks. */649 {650 LPDIRECTSOUNDBUFFER pDSB = NULL;651 652 DSBUFFERDESC bd;653 RT_ZERO(bd);654 bd.dwSize = sizeof(bd);655 bd.lpwfxFormat = &wfx;656 657 /*658 * As we reuse our (secondary) buffer for playing out data as it comes in,659 * we're using this buffer as a so-called streaming buffer.660 *661 * See https://msdn.microsoft.com/en-us/library/windows/desktop/ee419014(v=vs.85).aspx662 *663 * However, as we do not want to use memory on the sound device directly664 * (as most modern audio hardware on the host doesn't have this anyway),665 * we're *not* going to use DSBCAPS_STATIC for that.666 *667 * Instead we're specifying DSBCAPS_LOCSOFTWARE, as this fits the bill668 * of copying own buffer data to our secondary's Direct Sound buffer.669 */670 bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE;671 bd.dwBufferBytes = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize);672 673 DSLOG(("DSound: Requested playback buffer is %RU64ms (%ld bytes)\n",674 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes));675 676 hr = IDirectSound8_CreateSoundBuffer(pThis->pDS, &bd, &pDSB, NULL);677 if (FAILED(hr))678 {679 DSLOGREL(("DSound: Creating playback sound buffer failed with %Rhrc\n", hr));680 break;681 }682 683 /* "Upgrade" to IDirectSoundBuffer8 interface. */684 hr = IDirectSoundBuffer_QueryInterface(pDSB, IID_IDirectSoundBuffer8, (PVOID *)&pStreamDS->Out.pDSB);685 IDirectSoundBuffer_Release(pDSB);686 if (FAILED(hr))687 {688 DSLOGREL(("DSound: Querying playback sound buffer interface failed with %Rhrc\n", hr));689 break;690 }691 692 /*693 * Query the actual parameters set for this stream.694 * Those might be different than the initially requested parameters.695 */696 RT_ZERO(wfx);697 hr = IDirectSoundBuffer8_GetFormat(pStreamDS->Out.pDSB, &wfx, sizeof(wfx), NULL);698 if (FAILED(hr))699 {700 DSLOGREL(("DSound: Getting playback format failed with %Rhrc\n", hr));701 break;702 }703 704 DSBCAPS bc;705 RT_ZERO(bc);706 bc.dwSize = sizeof(bc);707 708 hr = IDirectSoundBuffer8_GetCaps(pStreamDS->Out.pDSB, &bc);709 if (FAILED(hr))710 {711 DSLOGREL(("DSound: Getting playback capabilities failed with %Rhrc\n", hr));712 break;713 }714 715 DSLOG(("DSound: Acquired playback buffer is %RU64ms (%ld bytes)\n",716 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes));717 718 DSLOG(("DSound: Acquired playback format:\n"719 " dwBufferBytes = %RI32\n"720 " dwFlags = 0x%x\n"721 " wFormatTag = %RI16\n"722 " nChannels = %RI16\n"723 " nSamplesPerSec = %RU32\n"724 " nAvgBytesPerSec = %RU32\n"725 " nBlockAlign = %RI16\n"726 " wBitsPerSample = %RI16\n"727 " cbSize = %RI16\n",728 bc.dwBufferBytes,729 bc.dwFlags,730 wfx.wFormatTag,731 wfx.nChannels,732 wfx.nSamplesPerSec,733 wfx.nAvgBytesPerSec,734 wfx.nBlockAlign,735 wfx.wBitsPerSample,736 wfx.cbSize));737 738 if (bc.dwBufferBytes & pStreamDS->uAlign)739 DSLOGREL(("DSound: Playback capabilities returned misaligned buffer: size %RU32, alignment %RU32\n",740 bc.dwBufferBytes, pStreamDS->uAlign + 1));741 742 /*743 * Initial state.744 * dsoundPlayStart initializes part of it to make sure that Stop/Start continues with a correct745 * playback buffer position.746 */747 pStreamDS->cbBufSize = bc.dwBufferBytes;748 749 pThis->pDSStrmOut = pStreamDS;750 751 const uint32_t cfBufSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize);752 753 pCfgAcq->Backend.cFramesBufferSize = cfBufSize;754 pCfgAcq->Backend.cFramesPeriod = cfBufSize / 4;755 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;756 757 } while (0);758 759 if (FAILED(hr))760 directSoundPlayClose(pThis, pStreamDS);761 762 return hr;763 }764 765 static void dsoundPlayClearBuffer(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)766 {767 AssertPtrReturnVoid(pStreamDS);768 769 PPDMAUDIOPCMPROPS pProps = &pStreamDS->Cfg.Props;770 771 HRESULT hr = IDirectSoundBuffer_SetCurrentPosition(pStreamDS->Out.pDSB, 0 /* Position */);772 if (FAILED(hr))773 DSLOGREL(("DSound: Setting current position to 0 when clearing buffer failed with %Rhrc\n", hr));774 775 PVOID pv1;776 hr = directSoundPlayLock(pThis, pStreamDS,777 0 /* dwOffset */, pStreamDS->cbBufSize,778 &pv1, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);779 if (SUCCEEDED(hr))780 {781 PDMAudioPropsClearBuffer(pProps, pv1, pStreamDS->cbBufSize, PDMAUDIOPCMPROPS_B2F(pProps, pStreamDS->cbBufSize));782 783 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, NULL, 0, 0);784 785 /* Make sure to get the last playback position and current write position from DirectSound again.786 * Those positions in theory could have changed, re-fetch them to be sure. */787 DWORD offPlay = 0;788 DWORD offWrite = 0;789 hr = IDirectSoundBuffer_GetCurrentPosition(pStreamDS->Out.pDSB, &offPlay, &offWrite);790 if (SUCCEEDED(hr))791 {792 pStreamDS->Out.offPlayCursorLastPlayed = offPlay;793 pStreamDS->Out.offWritePos = offWrite;794 }795 else796 DSLOGREL(("DSound: Re-fetching current position when clearing buffer failed with %Rhrc\n", hr));797 }798 }799 476 800 477 static HRESULT directSoundPlayGetStatus(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB, DWORD *pdwStatus) … … 828 505 else 829 506 DSLOGREL(("DSound: Retrieving playback status failed with %Rhrc\n", hr)); 830 831 return hr;832 }833 834 static HRESULT directSoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush)835 {836 AssertPtrReturn(pThis, E_POINTER);837 AssertPtrReturn(pStreamDS, E_POINTER);838 839 HRESULT hr = S_OK;840 841 if (pStreamDS->Out.pDSB)842 {843 DSLOG(("DSound: Stopping playback\n"));844 hr = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);845 if (FAILED(hr))846 {847 hr = directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);848 if (FAILED(hr)) /** @todo shouldn't this be a SUCCEEDED? */849 hr = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);850 }851 }852 853 if (SUCCEEDED(hr))854 {855 if (fFlush)856 dsoundStreamReset(pThis, pStreamDS);857 }858 859 if (FAILED(hr))860 DSLOGREL(("DSound: %s playback failed with %Rhrc\n", fFlush ? "Stopping" : "Pausing", hr));861 862 return hr;863 }864 865 /**866 * Enables or disables a stream.867 *868 * @return IPRT status code.869 * @param pThis Host audio driver instance.870 * @param pStreamDS Stream to enable / disable.871 * @param fEnable Whether to enable or disable the stream.872 */873 static int dsoundStreamEnable(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fEnable)874 {875 RT_NOREF(pThis);876 877 LogFunc(("%s %s\n",878 fEnable ? "Enabling" : "Disabling",879 pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN ? "capture" : "playback"));880 881 if (fEnable)882 dsoundStreamReset(pThis, pStreamDS);883 884 pStreamDS->fEnabled = fEnable;885 886 return VINF_SUCCESS;887 }888 889 890 /**891 * Resets the state of a DirectSound stream.892 *893 * @param pThis Host audio driver instance.894 * @param pStreamDS Stream to reset state for.895 */896 static void dsoundStreamReset(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)897 {898 RT_NOREF(pThis);899 900 LogFunc(("Resetting %s\n",901 pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN ? "capture" : "playback"));902 903 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)904 {905 pStreamDS->In.offReadPos = 0;906 pStreamDS->In.cOverruns = 0;907 908 /* Also reset the DirectSound Capture Buffer (DSCB) by clearing all data to make sure909 * not stale audio data is left. */910 if (pStreamDS->In.pDSCB)911 {912 PVOID pv1; PVOID pv2; DWORD cb1; DWORD cb2;913 HRESULT hr = directSoundCaptureLock(pStreamDS, 0 /* Offset */, pStreamDS->cbBufSize, &pv1, &pv2, &cb1, &cb2,914 0 /* Flags */);915 if (SUCCEEDED(hr))916 {917 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1));918 if (pv2 && cb2)919 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2));920 directSoundCaptureUnlock(pStreamDS->In.pDSCB, pv1, pv2, cb1, cb2);921 }922 }923 }924 else if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)925 {926 /* If draining was enagaged, make sure dsound has stopped playing. */927 if (pStreamDS->Out.fDrain && pStreamDS->Out.pDSB)928 pStreamDS->Out.pDSB->Stop();929 930 pStreamDS->Out.fFirstTransfer = true;931 pStreamDS->Out.fDrain = false;932 pStreamDS->Out.cUnderruns = 0;933 934 pStreamDS->Out.cbLastTransferred = 0;935 pStreamDS->Out.tsLastTransferredMs = 0;936 937 pStreamDS->Out.cbTransferred = 0;938 pStreamDS->Out.cbWritten = 0;939 940 pStreamDS->Out.offWritePos = 0;941 pStreamDS->Out.offPlayCursorLastPending = 0;942 pStreamDS->Out.offPlayCursorLastPlayed = 0;943 944 /* Also reset the DirectSound Buffer (DSB) by setting the position to 0 and clear all data to make sure945 * not stale audio data is left. */946 if (pStreamDS->Out.pDSB)947 {948 HRESULT hr = IDirectSoundBuffer8_SetCurrentPosition(pStreamDS->Out.pDSB, 0);949 if (SUCCEEDED(hr))950 {951 PVOID pv1; PVOID pv2; DWORD cb1; DWORD cb2;952 hr = directSoundPlayLock(pThis, pStreamDS, 0 /* Offset */, pStreamDS->cbBufSize, &pv1, &pv2, &cb1, &cb2,953 0 /* Flags */);954 if (SUCCEEDED(hr))955 {956 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1));957 if (pv2 && cb2)958 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2));959 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2);960 }961 }962 }963 }964 965 #ifdef LOG_ENABLED966 pStreamDS->Dbg.tsLastTransferredMs = 0;967 #endif968 }969 970 971 /**972 * Starts playing a DirectSound stream.973 *974 * @return HRESULT975 * @param pThis Host audio driver instance.976 * @param pStreamDS Stream to start playing.977 */978 static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)979 {980 HRESULT hr = S_OK;981 982 DWORD fFlags = DSCBSTART_LOOPING;983 984 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)985 {986 DSLOG(("DSound: Starting playback\n"));987 hr = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, fFlags);988 if ( SUCCEEDED(hr)989 || hr != DSERR_BUFFERLOST)990 break;991 LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));992 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);993 }994 507 995 508 return hr; … … 1056 569 #endif 1057 570 571 1058 572 /** 1059 573 * Destroys a DirectSound capture interface. … … 1071 585 } 1072 586 } 587 1073 588 1074 589 /** … … 1115 630 } 1116 631 1117 static HRESULT directSoundCaptureClose(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1118 { 1119 AssertPtrReturn(pThis, E_POINTER); 1120 AssertPtrReturn(pStreamDS, E_POINTER); 1121 632 633 /** 634 * Updates this host driver's internal status, according to the global, overall input/output 635 * state and all connected (native) audio streams. 636 * 637 * @todo r=bird: This is a 'ing waste of 'ing time! We're doing this everytime 638 * an 'ing stream is created and we doesn't 'ing use the information here 639 * for any darn thing! Given the reported slowness of enumeration and 640 * issues with the 'ing code the only appropriate response is: 641 * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARG!!!!!!! 642 * 643 * @param pThis Host audio driver instance. 644 */ 645 static void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis) 646 { 647 #if 0 /** @todo r=bird: This isn't doing *ANYTHING* useful. So, I've just disabled it. */ 648 AssertPtrReturnVoid(pThis); 1122 649 LogFlowFuncEnter(); 1123 650 1124 HRESULT hr = directSoundCaptureStop(pThis, pStreamDS, true /* fFlush */); 1125 if (FAILED(hr)) 1126 return hr; 1127 1128 if ( pStreamDS 1129 && pStreamDS->In.pDSCB) 1130 { 1131 DSLOG(("DSound: Closing capturing stream\n")); 1132 1133 IDirectSoundCaptureBuffer8_Release(pStreamDS->In.pDSCB); 1134 pStreamDS->In.pDSCB = NULL; 1135 } 1136 1137 LogFlowFunc(("Returning %Rhrc\n", hr)); 1138 return hr; 1139 } 1140 1141 static HRESULT directSoundCaptureOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, 1142 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1143 { 1144 AssertPtrReturn(pThis, E_POINTER); 1145 AssertPtrReturn(pStreamDS, E_POINTER); 1146 AssertPtrReturn(pCfgReq, E_POINTER); 1147 AssertPtrReturn(pCfgAcq, E_POINTER); 1148 1149 /** @todo r=bird: I cannot see any code populating pCfgAcq... */ 1150 1151 LogFlowFuncEnter(); 1152 1153 Assert(pStreamDS->In.pDSCB == NULL); 1154 1155 DSLOG(("DSound: Opening capturing stream (uHz=%RU32, cChannels=%RU8, cBits=%u, fSigned=%RTbool)\n", pCfgReq->Props.uHz, 1156 PDMAudioPropsChannels(&pCfgReq->Props), PDMAudioPropsSampleBits(&pCfgReq->Props), pCfgReq->Props.fSigned)); 1157 1158 WAVEFORMATEX wfx; 1159 int rc = dsoundWaveFmtFromCfg(pCfgReq, &wfx); 1160 if (RT_FAILURE(rc)) 1161 return E_INVALIDARG; 1162 1163 /** @todo r=bird: Why is this called every time? It triggers a device 1164 * enumeration. Andy claimed on IRC that enumeration was only done once... */ 1165 dsoundUpdateStatusInternal(pThis); 1166 1167 HRESULT hr = directSoundCaptureInterfaceCreate(pThis->Cfg.pGuidCapture, &pThis->pDSC); 1168 if (FAILED(hr)) 1169 return hr; 1170 1171 do /* For readability breaks... */ 1172 { 1173 DSCBUFFERDESC bd; 1174 RT_ZERO(bd); 1175 1176 bd.dwSize = sizeof(bd); 1177 bd.lpwfxFormat = &wfx; 1178 bd.dwBufferBytes = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize); 1179 1180 DSLOG(("DSound: Requested capture buffer is %RU64ms (%ld bytes)\n", 1181 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes)); 1182 1183 LPDIRECTSOUNDCAPTUREBUFFER pDSCB; 1184 hr = IDirectSoundCapture_CreateCaptureBuffer(pThis->pDSC, &bd, &pDSCB, NULL); 1185 if (FAILED(hr)) 1186 { 1187 if (hr == E_ACCESSDENIED) 1188 { 1189 DSLOGREL(("DSound: Capturing input from host not possible, access denied\n")); 1190 } 1191 else 1192 DSLOGREL(("DSound: Creating capture buffer failed with %Rhrc\n", hr)); 1193 break; 1194 } 1195 1196 hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pStreamDS->In.pDSCB); 1197 IDirectSoundCaptureBuffer_Release(pDSCB); 1198 if (FAILED(hr)) 1199 { 1200 DSLOGREL(("DSound: Querying interface for capture buffer failed with %Rhrc\n", hr)); 1201 break; 1202 } 1203 1204 /* 1205 * Query the actual parameters. 1206 */ 1207 DWORD offByteReadPos = 0; 1208 hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pStreamDS->In.pDSCB, NULL, &offByteReadPos); 1209 if (FAILED(hr)) 1210 { 1211 offByteReadPos = 0; 1212 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr)); 1213 } 1214 1215 RT_ZERO(wfx); 1216 hr = IDirectSoundCaptureBuffer8_GetFormat(pStreamDS->In.pDSCB, &wfx, sizeof(wfx), NULL); 1217 if (FAILED(hr)) 1218 { 1219 DSLOGREL(("DSound: Getting capture format failed with %Rhrc\n", hr)); 1220 break; 1221 } 1222 1223 DSCBCAPS bc; 1224 RT_ZERO(bc); 1225 bc.dwSize = sizeof(bc); 1226 hr = IDirectSoundCaptureBuffer8_GetCaps(pStreamDS->In.pDSCB, &bc); 1227 if (FAILED(hr)) 1228 { 1229 DSLOGREL(("DSound: Getting capture capabilities failed with %Rhrc\n", hr)); 1230 break; 1231 } 1232 1233 DSLOG(("DSound: Acquired capture buffer is %RU64ms (%ld bytes)\n", 1234 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes)); 1235 1236 DSLOG(("DSound: Capture format:\n" 1237 " dwBufferBytes = %RI32\n" 1238 " dwFlags = 0x%x\n" 1239 " wFormatTag = %RI16\n" 1240 " nChannels = %RI16\n" 1241 " nSamplesPerSec = %RU32\n" 1242 " nAvgBytesPerSec = %RU32\n" 1243 " nBlockAlign = %RI16\n" 1244 " wBitsPerSample = %RI16\n" 1245 " cbSize = %RI16\n", 1246 bc.dwBufferBytes, 1247 bc.dwFlags, 1248 wfx.wFormatTag, 1249 wfx.nChannels, 1250 wfx.nSamplesPerSec, 1251 wfx.nAvgBytesPerSec, 1252 wfx.nBlockAlign, 1253 wfx.wBitsPerSample, 1254 wfx.cbSize)); 1255 1256 if (bc.dwBufferBytes & pStreamDS->uAlign) 1257 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n", 1258 bc.dwBufferBytes, pStreamDS->uAlign + 1)); 1259 1260 /* Initial state: reading at the initial capture position, no error. */ 1261 pStreamDS->In.offReadPos = 0; 1262 pStreamDS->cbBufSize = bc.dwBufferBytes; 1263 1264 pThis->pDSStrmIn = pStreamDS; 1265 1266 pCfgAcq->Backend.cFramesBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize); 1267 1268 } while (0); 1269 1270 if (FAILED(hr)) 1271 directSoundCaptureClose(pThis, pStreamDS); 1272 1273 LogFlowFunc(("Returning %Rhrc\n", hr)); 1274 return hr; 1275 } 1276 1277 static HRESULT directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush) 1278 { 1279 AssertPtrReturn(pThis, E_POINTER); 1280 AssertPtrReturn(pStreamDS, E_POINTER); 1281 651 PDMAudioHostEnumDelete(&pThis->DeviceEnum); 652 int rc = dsoundDevicesEnumerate(pThis, &pThis->DeviceEnum); 653 if (RT_SUCCESS(rc)) 654 { 655 #if 0 656 if ( pThis->fEnabledOut != RT_BOOL(cbCtx.cDevOut) 657 || pThis->fEnabledIn != RT_BOOL(cbCtx.cDevIn)) 658 { 659 /** @todo Use a registered callback to the audio connector (e.g "OnConfigurationChanged") to 660 * let the connector know that something has changed within the host backend. */ 661 } 662 #endif 663 pThis->fEnabledIn = PDMAudioHostEnumCountMatching(&pThis->DeviceEnum, PDMAUDIODIR_IN) != 0; 664 pThis->fEnabledOut = PDMAudioHostEnumCountMatching(&pThis->DeviceEnum, PDMAUDIODIR_OUT) != 0; 665 } 666 667 LogFlowFuncLeaveRC(rc); 668 #else 1282 669 RT_NOREF(pThis); 1283 1284 HRESULT hr = S_OK; 1285 1286 if (pStreamDS->In.pDSCB) 1287 { 1288 DSLOG(("DSound: Stopping capture\n")); 1289 hr = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB); 1290 } 1291 1292 if (SUCCEEDED(hr)) 1293 { 1294 if (fFlush) 1295 dsoundStreamReset(pThis, pStreamDS); 1296 } 1297 1298 if (FAILED(hr)) 1299 DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr)); 1300 1301 return hr; 1302 } 1303 1304 static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1305 { 1306 AssertPtrReturn(pThis, E_POINTER); 1307 AssertPtrReturn(pStreamDS, E_POINTER); 1308 1309 HRESULT hr; 1310 if (pStreamDS->In.pDSCB) 1311 { 1312 DWORD dwStatus; 1313 hr = IDirectSoundCaptureBuffer8_GetStatus(pStreamDS->In.pDSCB, &dwStatus); 1314 if (FAILED(hr)) 1315 { 1316 DSLOGREL(("DSound: Retrieving capture status failed with %Rhrc\n", hr)); 1317 } 1318 else 1319 { 1320 if (dwStatus & DSCBSTATUS_CAPTURING) 1321 { 1322 DSLOG(("DSound: Already capturing\n")); 1323 } 1324 else 1325 { 1326 const DWORD fFlags = DSCBSTART_LOOPING; 1327 1328 DSLOG(("DSound: Starting to capture\n")); 1329 hr = IDirectSoundCaptureBuffer8_Start(pStreamDS->In.pDSCB, fFlags); 1330 if (FAILED(hr)) 1331 DSLOGREL(("DSound: Starting to capture failed with %Rhrc\n", hr)); 1332 } 1333 } 1334 } 1335 else 1336 hr = E_UNEXPECTED; 1337 1338 LogFlowFunc(("Returning %Rhrc\n", hr)); 1339 return hr; 1340 } 670 #endif 671 } 672 673 674 /********************************************************************************************************************************* 675 * PDMIHOSTAUDIO * 676 *********************************************************************************************************************************/ 677 678 /** 679 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig} 680 */ 681 static DECLCALLBACK(int) drvHostDSoundHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg) 682 { 683 RT_NOREF(pInterface); 684 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 685 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER); 686 687 RT_BZERO(pBackendCfg, sizeof(PPDMAUDIOBACKENDCFG)); 688 689 pBackendCfg->cbStreamOut = sizeof(DSOUNDSTREAM); 690 pBackendCfg->cbStreamIn = sizeof(DSOUNDSTREAM); 691 692 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "DirectSound"); 693 694 pBackendCfg->cMaxStreamsIn = UINT32_MAX; 695 pBackendCfg->cMaxStreamsOut = UINT32_MAX; 696 697 return VINF_SUCCESS; 698 } 699 1341 700 1342 701 /** … … 1399 758 } 1400 759 760 1401 761 /** 1402 762 * Callback for the capture device enumeration. … … 1455 815 } 1456 816 1457 /** 1458 * Qqueries information for a given (DirectSound) device. 817 818 /** 819 * Queries information for a given (DirectSound) device. 1459 820 * 1460 821 * @returns VBox status code. … … 1657 1018 } 1658 1019 1020 1659 1021 /** 1660 1022 * Does a (Re-)enumeration of the host's playback + capturing devices. 1661 1023 * 1662 * @return IPRTstatus code.1024 * @return VBox status code. 1663 1025 * @param pThis Host audio driver instance. 1664 1026 * @param pDevEnm Where to store the enumerated devices. … … 1794 1156 } 1795 1157 1796 /** 1797 * Updates this host driver's internal status, according to the global, overall input/output 1798 * state and all connected (native) audio streams. 1799 * 1800 * @todo r=bird: This is a 'ing waste of 'ing time! We're doing this everytime 1801 * an 'ing stream is created and we doesn't 'ing use the information here 1802 * for any darn thing! Given the reported slowness of enumeration and 1803 * issues with eh 'ing code the only appropriate response is: 1804 * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARG!!!!!!! 1158 1159 /** 1160 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetDevices} 1161 */ 1162 static DECLCALLBACK(int) drvHostDSoundHA_GetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHOSTENUM pDeviceEnum) 1163 { 1164 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1165 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER); 1166 1167 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface); 1168 1169 int rc = RTCritSectEnter(&pThis->CritSect); 1170 if (RT_SUCCESS(rc)) 1171 { 1172 PDMAudioHostEnumInit(pDeviceEnum); 1173 rc = dsoundDevicesEnumerate(pThis, pDeviceEnum); 1174 if (RT_FAILURE(rc)) 1175 PDMAudioHostEnumDelete(pDeviceEnum); 1176 1177 int rc2 = RTCritSectLeave(&pThis->CritSect); 1178 AssertRC(rc2); 1179 } 1180 1181 LogFlowFunc(("Returning %Rrc\n", rc)); 1182 return rc; 1183 } 1184 1185 1186 /** 1187 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus} 1188 */ 1189 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDSoundHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir) 1190 { 1191 RT_NOREF(enmDir); 1192 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN); 1193 1194 return PDMAUDIOBACKENDSTS_RUNNING; 1195 } 1196 1197 1198 static int dsoundWaveFmtFromCfg(PPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt) 1199 { 1200 AssertPtrReturn(pCfg, VERR_INVALID_POINTER); 1201 AssertPtrReturn(pFmt, VERR_INVALID_POINTER); 1202 1203 RT_BZERO(pFmt, sizeof(WAVEFORMATEX)); 1204 1205 pFmt->wFormatTag = WAVE_FORMAT_PCM; 1206 pFmt->nChannels = PDMAudioPropsChannels(&pCfg->Props); 1207 pFmt->wBitsPerSample = PDMAudioPropsSampleBits(&pCfg->Props); 1208 pFmt->nSamplesPerSec = PDMAudioPropsHz(&pCfg->Props); 1209 pFmt->nBlockAlign = PDMAudioPropsFrameSize(&pCfg->Props); 1210 pFmt->nAvgBytesPerSec = PDMAudioPropsFramesToBytes(&pCfg->Props, PDMAudioPropsHz(&pCfg->Props)); 1211 pFmt->cbSize = 0; /* No extra data specified. */ 1212 1213 return VINF_SUCCESS; 1214 } 1215 1216 1217 /** 1218 * Resets the state of a DirectSound stream. 1805 1219 * 1806 1220 * @param pThis Host audio driver instance. 1807 */ 1808 static void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis) 1809 { 1810 #if 0 /** @todo r=bird: This isn't doing *ANYTHING* useful. So, I've just disabled it. */ 1811 AssertPtrReturnVoid(pThis); 1221 * @param pStreamDS Stream to reset state for. 1222 */ 1223 static void dsoundStreamReset(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1224 { 1225 RT_NOREF(pThis); 1226 1227 LogFunc(("Resetting %s\n", 1228 pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN ? "capture" : "playback")); 1229 1230 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN) 1231 { 1232 pStreamDS->In.offReadPos = 0; 1233 pStreamDS->In.cOverruns = 0; 1234 1235 /* Also reset the DirectSound Capture Buffer (DSCB) by clearing all data to make sure 1236 * not stale audio data is left. */ 1237 if (pStreamDS->In.pDSCB) 1238 { 1239 PVOID pv1; PVOID pv2; DWORD cb1; DWORD cb2; 1240 HRESULT hr = directSoundCaptureLock(pStreamDS, 0 /* Offset */, pStreamDS->cbBufSize, &pv1, &pv2, &cb1, &cb2, 1241 0 /* Flags */); 1242 if (SUCCEEDED(hr)) 1243 { 1244 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1)); 1245 if (pv2 && cb2) 1246 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2)); 1247 directSoundCaptureUnlock(pStreamDS->In.pDSCB, pv1, pv2, cb1, cb2); 1248 } 1249 } 1250 } 1251 else if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT) 1252 { 1253 /* If draining was enagaged, make sure dsound has stopped playing. */ 1254 if (pStreamDS->Out.fDrain && pStreamDS->Out.pDSB) 1255 pStreamDS->Out.pDSB->Stop(); 1256 1257 pStreamDS->Out.fFirstTransfer = true; 1258 pStreamDS->Out.fDrain = false; 1259 pStreamDS->Out.cUnderruns = 0; 1260 1261 pStreamDS->Out.cbLastTransferred = 0; 1262 pStreamDS->Out.tsLastTransferredMs = 0; 1263 1264 pStreamDS->Out.cbTransferred = 0; 1265 pStreamDS->Out.cbWritten = 0; 1266 1267 pStreamDS->Out.offWritePos = 0; 1268 pStreamDS->Out.offPlayCursorLastPending = 0; 1269 pStreamDS->Out.offPlayCursorLastPlayed = 0; 1270 1271 /* Also reset the DirectSound Buffer (DSB) by setting the position to 0 and clear all data to make sure 1272 * not stale audio data is left. */ 1273 if (pStreamDS->Out.pDSB) 1274 { 1275 HRESULT hr = IDirectSoundBuffer8_SetCurrentPosition(pStreamDS->Out.pDSB, 0); 1276 if (SUCCEEDED(hr)) 1277 { 1278 PVOID pv1; PVOID pv2; DWORD cb1; DWORD cb2; 1279 hr = directSoundPlayLock(pThis, pStreamDS, 0 /* Offset */, pStreamDS->cbBufSize, &pv1, &pv2, &cb1, &cb2, 1280 0 /* Flags */); 1281 if (SUCCEEDED(hr)) 1282 { 1283 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1)); 1284 if (pv2 && cb2) 1285 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2)); 1286 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2); 1287 } 1288 } 1289 } 1290 } 1291 1292 #ifdef LOG_ENABLED 1293 pStreamDS->Dbg.tsLastTransferredMs = 0; 1294 #endif 1295 } 1296 1297 1298 static HRESULT directSoundCaptureClose(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1299 { 1300 AssertPtrReturn(pThis, E_POINTER); 1301 AssertPtrReturn(pStreamDS, E_POINTER); 1302 1812 1303 LogFlowFuncEnter(); 1813 1304 1814 PDMAudioHostEnumDelete(&pThis->DeviceEnum); 1815 int rc = dsoundDevicesEnumerate(pThis, &pThis->DeviceEnum); 1816 if (RT_SUCCESS(rc)) 1817 { 1818 #if 0 1819 if ( pThis->fEnabledOut != RT_BOOL(cbCtx.cDevOut) 1820 || pThis->fEnabledIn != RT_BOOL(cbCtx.cDevIn)) 1821 { 1822 /** @todo Use a registered callback to the audio connector (e.g "OnConfigurationChanged") to 1823 * let the connector know that something has changed within the host backend. */ 1824 } 1825 #endif 1826 pThis->fEnabledIn = PDMAudioHostEnumCountMatching(&pThis->DeviceEnum, PDMAUDIODIR_IN) != 0; 1827 pThis->fEnabledOut = PDMAudioHostEnumCountMatching(&pThis->DeviceEnum, PDMAUDIODIR_OUT) != 0; 1828 } 1829 1830 LogFlowFuncLeaveRC(rc); 1831 #else 1832 RT_NOREF(pThis); 1833 #endif 1834 } 1305 HRESULT hr = directSoundCaptureStop(pThis, pStreamDS, true /* fFlush */); 1306 if (FAILED(hr)) 1307 return hr; 1308 1309 if ( pStreamDS 1310 && pStreamDS->In.pDSCB) 1311 { 1312 DSLOG(("DSound: Closing capturing stream\n")); 1313 1314 IDirectSoundCaptureBuffer8_Release(pStreamDS->In.pDSCB); 1315 pStreamDS->In.pDSCB = NULL; 1316 } 1317 1318 LogFlowFunc(("Returning %Rhrc\n", hr)); 1319 return hr; 1320 } 1321 1322 1323 static HRESULT directSoundCaptureOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, 1324 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1325 { 1326 AssertPtrReturn(pThis, E_POINTER); 1327 AssertPtrReturn(pStreamDS, E_POINTER); 1328 AssertPtrReturn(pCfgReq, E_POINTER); 1329 AssertPtrReturn(pCfgAcq, E_POINTER); 1330 1331 /** @todo r=bird: I cannot see any code populating pCfgAcq... */ 1332 1333 LogFlowFuncEnter(); 1334 1335 Assert(pStreamDS->In.pDSCB == NULL); 1336 1337 DSLOG(("DSound: Opening capturing stream (uHz=%RU32, cChannels=%RU8, cBits=%u, fSigned=%RTbool)\n", pCfgReq->Props.uHz, 1338 PDMAudioPropsChannels(&pCfgReq->Props), PDMAudioPropsSampleBits(&pCfgReq->Props), pCfgReq->Props.fSigned)); 1339 1340 WAVEFORMATEX wfx; 1341 int rc = dsoundWaveFmtFromCfg(pCfgReq, &wfx); 1342 if (RT_FAILURE(rc)) 1343 return E_INVALIDARG; 1344 1345 /** @todo r=bird: Why is this called every time? It triggers a device 1346 * enumeration. Andy claimed on IRC that enumeration was only done once... */ 1347 dsoundUpdateStatusInternal(pThis); 1348 1349 HRESULT hr = directSoundCaptureInterfaceCreate(pThis->Cfg.pGuidCapture, &pThis->pDSC); 1350 if (FAILED(hr)) 1351 return hr; 1352 1353 do /* For readability breaks... */ 1354 { 1355 DSCBUFFERDESC bd; 1356 RT_ZERO(bd); 1357 1358 bd.dwSize = sizeof(bd); 1359 bd.lpwfxFormat = &wfx; 1360 bd.dwBufferBytes = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize); 1361 1362 DSLOG(("DSound: Requested capture buffer is %RU64ms (%ld bytes)\n", 1363 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes)); 1364 1365 LPDIRECTSOUNDCAPTUREBUFFER pDSCB; 1366 hr = IDirectSoundCapture_CreateCaptureBuffer(pThis->pDSC, &bd, &pDSCB, NULL); 1367 if (FAILED(hr)) 1368 { 1369 if (hr == E_ACCESSDENIED) 1370 { 1371 DSLOGREL(("DSound: Capturing input from host not possible, access denied\n")); 1372 } 1373 else 1374 DSLOGREL(("DSound: Creating capture buffer failed with %Rhrc\n", hr)); 1375 break; 1376 } 1377 1378 hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pStreamDS->In.pDSCB); 1379 IDirectSoundCaptureBuffer_Release(pDSCB); 1380 if (FAILED(hr)) 1381 { 1382 DSLOGREL(("DSound: Querying interface for capture buffer failed with %Rhrc\n", hr)); 1383 break; 1384 } 1385 1386 /* 1387 * Query the actual parameters. 1388 */ 1389 DWORD offByteReadPos = 0; 1390 hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pStreamDS->In.pDSCB, NULL, &offByteReadPos); 1391 if (FAILED(hr)) 1392 { 1393 offByteReadPos = 0; 1394 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr)); 1395 } 1396 1397 RT_ZERO(wfx); 1398 hr = IDirectSoundCaptureBuffer8_GetFormat(pStreamDS->In.pDSCB, &wfx, sizeof(wfx), NULL); 1399 if (FAILED(hr)) 1400 { 1401 DSLOGREL(("DSound: Getting capture format failed with %Rhrc\n", hr)); 1402 break; 1403 } 1404 1405 DSCBCAPS bc; 1406 RT_ZERO(bc); 1407 bc.dwSize = sizeof(bc); 1408 hr = IDirectSoundCaptureBuffer8_GetCaps(pStreamDS->In.pDSCB, &bc); 1409 if (FAILED(hr)) 1410 { 1411 DSLOGREL(("DSound: Getting capture capabilities failed with %Rhrc\n", hr)); 1412 break; 1413 } 1414 1415 DSLOG(("DSound: Acquired capture buffer is %RU64ms (%ld bytes)\n", 1416 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes)); 1417 1418 DSLOG(("DSound: Capture format:\n" 1419 " dwBufferBytes = %RI32\n" 1420 " dwFlags = 0x%x\n" 1421 " wFormatTag = %RI16\n" 1422 " nChannels = %RI16\n" 1423 " nSamplesPerSec = %RU32\n" 1424 " nAvgBytesPerSec = %RU32\n" 1425 " nBlockAlign = %RI16\n" 1426 " wBitsPerSample = %RI16\n" 1427 " cbSize = %RI16\n", 1428 bc.dwBufferBytes, 1429 bc.dwFlags, 1430 wfx.wFormatTag, 1431 wfx.nChannels, 1432 wfx.nSamplesPerSec, 1433 wfx.nAvgBytesPerSec, 1434 wfx.nBlockAlign, 1435 wfx.wBitsPerSample, 1436 wfx.cbSize)); 1437 1438 if (bc.dwBufferBytes & pStreamDS->uAlign) 1439 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n", 1440 bc.dwBufferBytes, pStreamDS->uAlign + 1)); 1441 1442 /* Initial state: reading at the initial capture position, no error. */ 1443 pStreamDS->In.offReadPos = 0; 1444 pStreamDS->cbBufSize = bc.dwBufferBytes; 1445 1446 pThis->pDSStrmIn = pStreamDS; 1447 1448 pCfgAcq->Backend.cFramesBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize); 1449 1450 } while (0); 1451 1452 if (FAILED(hr)) 1453 directSoundCaptureClose(pThis, pStreamDS); 1454 1455 LogFlowFunc(("Returning %Rhrc\n", hr)); 1456 return hr; 1457 } 1458 1459 1460 static HRESULT directSoundPlayClose(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1461 { 1462 AssertPtrReturn(pThis, E_POINTER); 1463 AssertPtrReturn(pStreamDS, E_POINTER); 1464 1465 LogFlowFuncEnter(); 1466 1467 HRESULT hr = directSoundPlayStop(pThis, pStreamDS, true /* fFlush */); 1468 if (SUCCEEDED(hr)) 1469 { 1470 DSLOG(("DSound: Closing playback stream\n")); 1471 RTCritSectEnter(&pThis->CritSect); 1472 1473 if (pStreamDS->Out.pDSB) 1474 { 1475 IDirectSoundBuffer8_Release(pStreamDS->Out.pDSB); 1476 pStreamDS->Out.pDSB = NULL; 1477 } 1478 1479 pThis->pDSStrmOut = NULL; 1480 1481 RTCritSectLeave(&pThis->CritSect); 1482 } 1483 1484 if (FAILED(hr)) 1485 DSLOGREL(("DSound: Stopping playback stream %p failed with %Rhrc\n", pStreamDS, hr)); 1486 1487 return hr; 1488 } 1489 1490 1491 static HRESULT directSoundPlayOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, 1492 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1493 { 1494 AssertPtrReturn(pThis, E_POINTER); 1495 AssertPtrReturn(pStreamDS, E_POINTER); 1496 AssertPtrReturn(pCfgReq, E_POINTER); 1497 AssertPtrReturn(pCfgAcq, E_POINTER); 1498 1499 /** @todo r=bird: I cannot see any code populating pCfgAcq... */ 1500 1501 LogFlowFuncEnter(); 1502 1503 Assert(pStreamDS->Out.pDSB == NULL); 1504 1505 DSLOG(("DSound: Opening playback stream (uHz=%RU32, cChannels=%RU8, cBits=%u, fSigned=%RTbool)\n", pCfgReq->Props.uHz, 1506 PDMAudioPropsChannels(&pCfgReq->Props), PDMAudioPropsSampleBits(&pCfgReq->Props), pCfgReq->Props.fSigned)); 1507 1508 WAVEFORMATEX wfx; 1509 int rc = dsoundWaveFmtFromCfg(pCfgReq, &wfx); 1510 if (RT_FAILURE(rc)) 1511 return E_INVALIDARG; 1512 1513 DSLOG(("DSound: Requested playback format:\n" 1514 " wFormatTag = %RI16\n" 1515 " nChannels = %RI16\n" 1516 " nSamplesPerSec = %RU32\n" 1517 " nAvgBytesPerSec = %RU32\n" 1518 " nBlockAlign = %RI16\n" 1519 " wBitsPerSample = %RI16\n" 1520 " cbSize = %RI16\n", 1521 wfx.wFormatTag, 1522 wfx.nChannels, 1523 wfx.nSamplesPerSec, 1524 wfx.nAvgBytesPerSec, 1525 wfx.nBlockAlign, 1526 wfx.wBitsPerSample, 1527 wfx.cbSize)); 1528 1529 /** @todo r=bird: Why is this called every time? It triggers a device 1530 * enumeration. Andy claimed on IRC that enumeration was only done once... 1531 * It's generally a 'ing waste of time here too, as we dont really use any of 1532 * the information we gather there. */ 1533 dsoundUpdateStatusInternal(pThis); 1534 1535 HRESULT hr = directSoundPlayInterfaceCreate(pThis->Cfg.pGuidPlay, &pThis->pDS); 1536 if (FAILED(hr)) 1537 return hr; 1538 1539 do /* To use breaks. */ 1540 { 1541 LPDIRECTSOUNDBUFFER pDSB = NULL; 1542 1543 DSBUFFERDESC bd; 1544 RT_ZERO(bd); 1545 bd.dwSize = sizeof(bd); 1546 bd.lpwfxFormat = &wfx; 1547 1548 /* 1549 * As we reuse our (secondary) buffer for playing out data as it comes in, 1550 * we're using this buffer as a so-called streaming buffer. 1551 * 1552 * See https://msdn.microsoft.com/en-us/library/windows/desktop/ee419014(v=vs.85).aspx 1553 * 1554 * However, as we do not want to use memory on the sound device directly 1555 * (as most modern audio hardware on the host doesn't have this anyway), 1556 * we're *not* going to use DSBCAPS_STATIC for that. 1557 * 1558 * Instead we're specifying DSBCAPS_LOCSOFTWARE, as this fits the bill 1559 * of copying own buffer data to our secondary's Direct Sound buffer. 1560 */ 1561 bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE; 1562 bd.dwBufferBytes = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize); 1563 1564 DSLOG(("DSound: Requested playback buffer is %RU64ms (%ld bytes)\n", 1565 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes)); 1566 1567 hr = IDirectSound8_CreateSoundBuffer(pThis->pDS, &bd, &pDSB, NULL); 1568 if (FAILED(hr)) 1569 { 1570 DSLOGREL(("DSound: Creating playback sound buffer failed with %Rhrc\n", hr)); 1571 break; 1572 } 1573 1574 /* "Upgrade" to IDirectSoundBuffer8 interface. */ 1575 hr = IDirectSoundBuffer_QueryInterface(pDSB, IID_IDirectSoundBuffer8, (PVOID *)&pStreamDS->Out.pDSB); 1576 IDirectSoundBuffer_Release(pDSB); 1577 if (FAILED(hr)) 1578 { 1579 DSLOGREL(("DSound: Querying playback sound buffer interface failed with %Rhrc\n", hr)); 1580 break; 1581 } 1582 1583 /* 1584 * Query the actual parameters set for this stream. 1585 * Those might be different than the initially requested parameters. 1586 */ 1587 RT_ZERO(wfx); 1588 hr = IDirectSoundBuffer8_GetFormat(pStreamDS->Out.pDSB, &wfx, sizeof(wfx), NULL); 1589 if (FAILED(hr)) 1590 { 1591 DSLOGREL(("DSound: Getting playback format failed with %Rhrc\n", hr)); 1592 break; 1593 } 1594 1595 DSBCAPS bc; 1596 RT_ZERO(bc); 1597 bc.dwSize = sizeof(bc); 1598 1599 hr = IDirectSoundBuffer8_GetCaps(pStreamDS->Out.pDSB, &bc); 1600 if (FAILED(hr)) 1601 { 1602 DSLOGREL(("DSound: Getting playback capabilities failed with %Rhrc\n", hr)); 1603 break; 1604 } 1605 1606 DSLOG(("DSound: Acquired playback buffer is %RU64ms (%ld bytes)\n", 1607 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes)); 1608 1609 DSLOG(("DSound: Acquired playback format:\n" 1610 " dwBufferBytes = %RI32\n" 1611 " dwFlags = 0x%x\n" 1612 " wFormatTag = %RI16\n" 1613 " nChannels = %RI16\n" 1614 " nSamplesPerSec = %RU32\n" 1615 " nAvgBytesPerSec = %RU32\n" 1616 " nBlockAlign = %RI16\n" 1617 " wBitsPerSample = %RI16\n" 1618 " cbSize = %RI16\n", 1619 bc.dwBufferBytes, 1620 bc.dwFlags, 1621 wfx.wFormatTag, 1622 wfx.nChannels, 1623 wfx.nSamplesPerSec, 1624 wfx.nAvgBytesPerSec, 1625 wfx.nBlockAlign, 1626 wfx.wBitsPerSample, 1627 wfx.cbSize)); 1628 1629 if (bc.dwBufferBytes & pStreamDS->uAlign) 1630 DSLOGREL(("DSound: Playback capabilities returned misaligned buffer: size %RU32, alignment %RU32\n", 1631 bc.dwBufferBytes, pStreamDS->uAlign + 1)); 1632 1633 /* 1634 * Initial state. 1635 * dsoundPlayStart initializes part of it to make sure that Stop/Start continues with a correct 1636 * playback buffer position. 1637 */ 1638 pStreamDS->cbBufSize = bc.dwBufferBytes; 1639 1640 pThis->pDSStrmOut = pStreamDS; 1641 1642 const uint32_t cfBufSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize); 1643 1644 pCfgAcq->Backend.cFramesBufferSize = cfBufSize; 1645 pCfgAcq->Backend.cFramesPeriod = cfBufSize / 4; 1646 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2; 1647 1648 } while (0); 1649 1650 if (FAILED(hr)) 1651 directSoundPlayClose(pThis, pStreamDS); 1652 1653 return hr; 1654 } 1655 1656 1657 static void dsoundPlayClearBuffer(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1658 { 1659 AssertPtrReturnVoid(pStreamDS); 1660 1661 PPDMAUDIOPCMPROPS pProps = &pStreamDS->Cfg.Props; 1662 1663 HRESULT hr = IDirectSoundBuffer_SetCurrentPosition(pStreamDS->Out.pDSB, 0 /* Position */); 1664 if (FAILED(hr)) 1665 DSLOGREL(("DSound: Setting current position to 0 when clearing buffer failed with %Rhrc\n", hr)); 1666 1667 PVOID pv1; 1668 hr = directSoundPlayLock(pThis, pStreamDS, 1669 0 /* dwOffset */, pStreamDS->cbBufSize, 1670 &pv1, NULL, 0, 0, DSBLOCK_ENTIREBUFFER); 1671 if (SUCCEEDED(hr)) 1672 { 1673 PDMAudioPropsClearBuffer(pProps, pv1, pStreamDS->cbBufSize, PDMAUDIOPCMPROPS_B2F(pProps, pStreamDS->cbBufSize)); 1674 1675 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, NULL, 0, 0); 1676 1677 /* Make sure to get the last playback position and current write position from DirectSound again. 1678 * Those positions in theory could have changed, re-fetch them to be sure. */ 1679 DWORD offPlay = 0; 1680 DWORD offWrite = 0; 1681 hr = IDirectSoundBuffer_GetCurrentPosition(pStreamDS->Out.pDSB, &offPlay, &offWrite); 1682 if (SUCCEEDED(hr)) 1683 { 1684 pStreamDS->Out.offPlayCursorLastPlayed = offPlay; 1685 pStreamDS->Out.offWritePos = offWrite; 1686 } 1687 else 1688 DSLOGREL(("DSound: Re-fetching current position when clearing buffer failed with %Rhrc\n", hr)); 1689 } 1690 } 1691 1835 1692 1836 1693 static int dsoundCreateStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, … … 1855 1712 return rc; 1856 1713 } 1714 1715 1716 static int dsoundCreateStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, 1717 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1718 { 1719 LogFunc(("pStreamDS=%p, pCfgReq=%p, enmRecSource=%s\n", 1720 pStreamDS, pCfgReq, PDMAudioRecSrcGetName(pCfgReq->u.enmSrc))); 1721 1722 1723 /* Try to open capture in case the device is already there. */ 1724 int rc; 1725 HRESULT hr = directSoundCaptureOpen(pThis, pStreamDS, pCfgReq, pCfgAcq); 1726 if (SUCCEEDED(hr)) 1727 { 1728 rc = PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq); 1729 if (RT_SUCCESS(rc)) 1730 dsoundStreamReset(pThis, pStreamDS); 1731 } 1732 else 1733 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE; 1734 1735 return rc; 1736 } 1737 1738 1739 /** 1740 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate} 1741 */ 1742 static DECLCALLBACK(int) drvHostDSoundHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 1743 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1744 { 1745 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1746 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 1747 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); 1748 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 1749 1750 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface); 1751 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 1752 1753 int rc; 1754 if (pCfgReq->enmDir == PDMAUDIODIR_IN) 1755 rc = dsoundCreateStreamIn(pThis, pStreamDS, pCfgReq, pCfgAcq); 1756 else 1757 rc = dsoundCreateStreamOut(pThis, pStreamDS, pCfgReq, pCfgAcq); 1758 1759 if (RT_SUCCESS(rc)) 1760 { 1761 /** @todo already copied */ 1762 rc = PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq); 1763 if (RT_SUCCESS(rc)) 1764 rc = RTCritSectInit(&pStreamDS->CritSect); 1765 } 1766 1767 return rc; 1768 } 1769 1770 1771 static int dsoundDestroyStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1772 { 1773 LogFlowFuncEnter(); 1774 1775 HRESULT hr = directSoundPlayStop(pThis, pStreamDS, true /* fFlush */); 1776 if (SUCCEEDED(hr)) 1777 { 1778 hr = directSoundPlayClose(pThis, pStreamDS); 1779 if (FAILED(hr)) 1780 return VERR_GENERAL_FAILURE; /** @todo Fix. */ 1781 } 1782 1783 return VINF_SUCCESS; 1784 } 1785 1786 1787 static int dsoundDestroyStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1788 { 1789 LogFlowFuncEnter(); 1790 1791 directSoundCaptureClose(pThis, pStreamDS); 1792 1793 return VINF_SUCCESS; 1794 } 1795 1796 1797 /** 1798 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy} 1799 */ 1800 static DECLCALLBACK(int) drvHostDSoundHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1801 { 1802 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1803 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 1804 1805 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface); 1806 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 1807 1808 int rc; 1809 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN) 1810 rc = dsoundDestroyStreamIn(pThis, pStreamDS); 1811 else 1812 rc = dsoundDestroyStreamOut(pThis, pStreamDS); 1813 1814 if (RT_SUCCESS(rc)) 1815 { 1816 if (RTCritSectIsInitialized(&pStreamDS->CritSect)) 1817 rc = RTCritSectDelete(&pStreamDS->CritSect); 1818 } 1819 1820 return rc; 1821 } 1822 1823 1824 /** 1825 * Enables or disables a stream. 1826 * 1827 * @return VBox status code. 1828 * @param pThis Host audio driver instance. 1829 * @param pStreamDS Stream to enable / disable. 1830 * @param fEnable Whether to enable or disable the stream. 1831 */ 1832 static int dsoundStreamEnable(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fEnable) 1833 { 1834 RT_NOREF(pThis); 1835 1836 LogFunc(("%s %s\n", 1837 fEnable ? "Enabling" : "Disabling", 1838 pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN ? "capture" : "playback")); 1839 1840 if (fEnable) 1841 dsoundStreamReset(pThis, pStreamDS); 1842 1843 pStreamDS->fEnabled = fEnable; 1844 1845 return VINF_SUCCESS; 1846 } 1847 1848 1849 /** 1850 * Starts playing a DirectSound stream. 1851 * 1852 * @return HRESULT 1853 * @param pThis Host audio driver instance. 1854 * @param pStreamDS Stream to start playing. 1855 */ 1856 static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1857 { 1858 HRESULT hr = S_OK; 1859 1860 DWORD fFlags = DSCBSTART_LOOPING; 1861 1862 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++) 1863 { 1864 DSLOG(("DSound: Starting playback\n")); 1865 hr = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, fFlags); 1866 if ( SUCCEEDED(hr) 1867 || hr != DSERR_BUFFERLOST) 1868 break; 1869 LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n")); 1870 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB); 1871 } 1872 1873 return hr; 1874 } 1875 1876 1877 static HRESULT directSoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush) 1878 { 1879 AssertPtrReturn(pThis, E_POINTER); 1880 AssertPtrReturn(pStreamDS, E_POINTER); 1881 1882 HRESULT hr = S_OK; 1883 1884 if (pStreamDS->Out.pDSB) 1885 { 1886 DSLOG(("DSound: Stopping playback\n")); 1887 hr = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB); 1888 if (FAILED(hr)) 1889 { 1890 hr = directSoundPlayRestore(pThis, pStreamDS->Out.pDSB); 1891 if (FAILED(hr)) /** @todo shouldn't this be a SUCCEEDED? */ 1892 hr = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB); 1893 } 1894 } 1895 1896 if (SUCCEEDED(hr)) 1897 { 1898 if (fFlush) 1899 dsoundStreamReset(pThis, pStreamDS); 1900 } 1901 1902 if (FAILED(hr)) 1903 DSLOGREL(("DSound: %s playback failed with %Rhrc\n", fFlush ? "Stopping" : "Pausing", hr)); 1904 1905 return hr; 1906 } 1907 1857 1908 1858 1909 static int dsoundControlStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PDMAUDIOSTREAMCMD enmStreamCmd) … … 1949 2000 } 1950 2001 1951 /** 1952 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay} 1953 */ 1954 static DECLCALLBACK(int) drvHostDSoundHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 1955 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 1956 { 1957 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); 1958 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 1959 AssertPtrReturn(pStreamDS, 0); 1960 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 1961 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 1962 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); 1963 1964 if (pStreamDS->fEnabled) 1965 AssertReturn(pStreamDS->cbBufSize, VERR_INTERNAL_ERROR_2); 2002 2003 static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 2004 { 2005 AssertPtrReturn(pThis, E_POINTER); 2006 AssertPtrReturn(pStreamDS, E_POINTER); 2007 2008 HRESULT hr; 2009 if (pStreamDS->In.pDSCB) 2010 { 2011 DWORD dwStatus; 2012 hr = IDirectSoundCaptureBuffer8_GetStatus(pStreamDS->In.pDSCB, &dwStatus); 2013 if (FAILED(hr)) 2014 { 2015 DSLOGREL(("DSound: Retrieving capture status failed with %Rhrc\n", hr)); 2016 } 2017 else 2018 { 2019 if (dwStatus & DSCBSTATUS_CAPTURING) 2020 { 2021 DSLOG(("DSound: Already capturing\n")); 2022 } 2023 else 2024 { 2025 const DWORD fFlags = DSCBSTART_LOOPING; 2026 2027 DSLOG(("DSound: Starting to capture\n")); 2028 hr = IDirectSoundCaptureBuffer8_Start(pStreamDS->In.pDSCB, fFlags); 2029 if (FAILED(hr)) 2030 DSLOGREL(("DSound: Starting to capture failed with %Rhrc\n", hr)); 2031 } 2032 } 2033 } 1966 2034 else 1967 { 1968 Log2Func(("Stream disabled, skipping\n")); 1969 return VINF_SUCCESS; 1970 } 1971 1972 /** @todo Any condition under which we should call dsoundUpdateStatusInternal(pThis) here? 1973 * The old code thought it did so in case of failure, only it couldn't ever fails, so it never did. */ 1974 1975 /* 1976 * Transfer loop. 1977 */ 1978 uint32_t cbWritten = 0; 1979 while (cbBuf > 0) 1980 { 1981 /* 1982 * Figure out how much we can possibly write. 1983 */ 1984 DWORD offPlayCursor = 0; 1985 DWORD cbWritable = 0; 1986 int rc = dsoundGetFreeOut(pThis, pStreamDS, &cbWritable, &offPlayCursor); 1987 AssertRCReturn(rc, rc); 1988 if (cbWritable < pStreamDS->Cfg.Props.cbFrame) 1989 break; 1990 1991 uint32_t const cbToWrite = RT_MIN(cbWritable, cbBuf); 1992 Log3Func(("offPlay=%#x offWritePos=%#x -> cbWritable=%#x cbToWrite=%#x%s%s\n", offPlayCursor, pStreamDS->Out.offWritePos, 1993 cbWritable, cbToWrite, pStreamDS->Out.fFirstTransfer ? " first" : "", pStreamDS->Out.fDrain ? " drain" : "")); 1994 1995 /* 1996 * Lock that amount of buffer. 1997 */ 1998 PVOID pv1 = NULL; 1999 DWORD cb1 = 0; 2000 PVOID pv2 = NULL; 2001 DWORD cb2 = 0; 2002 HRESULT hrc = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, cbToWrite, 2003 &pv1, &pv2, &cb1, &cb2, 0 /*dwFlags*/); 2004 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */ 2005 //AssertMsg(cb1 + cb2 == cbToWrite, ("%#x + %#x vs %#x\n", cb1, cb2, cbToWrite)); 2006 2007 /* 2008 * Copy over the data. 2009 */ 2010 memcpy(pv1, pvBuf, cb1); 2011 pvBuf = (uint8_t *)pvBuf + cb1; 2012 cbBuf -= cb1; 2013 cbWritten += cb1; 2014 2015 if (pv2) 2016 { 2017 memcpy(pv2, pvBuf, cb2); 2018 pvBuf = (uint8_t *)pvBuf + cb2; 2019 cbBuf -= cb2; 2020 cbWritten += cb2; 2021 } 2022 2023 /* 2024 * Unlock and update the write position. 2025 */ 2026 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2); /** @todo r=bird: pThis + pDSB parameters here for Unlock, but only pThis for Lock. Why? */ 2027 pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cb1 + cb2) % pStreamDS->cbBufSize; 2028 2029 /* 2030 * If this was the first chunk, kick off playing. 2031 */ 2032 if (!pStreamDS->Out.fFirstTransfer) 2033 { /* likely */ } 2034 else 2035 { 2036 *pcbWritten = cbWritten; 2037 hrc = directSoundPlayStart(pThis, pStreamDS); 2038 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */ 2039 pStreamDS->Out.fFirstTransfer = false; 2040 } 2041 } 2042 2043 /* 2044 * Done. 2045 */ 2046 *pcbWritten = cbWritten; 2047 2048 pStreamDS->Out.cbTransferred += cbWritten; 2049 if (cbWritten) 2050 { 2051 uint64_t const msPrev = pStreamDS->Out.tsLastTransferredMs; 2052 pStreamDS->Out.cbLastTransferred = cbWritten; 2053 pStreamDS->Out.tsLastTransferredMs = RTTimeMilliTS(); 2054 LogFlowFunc(("cbLastTransferred=%RU32, tsLastTransferredMs=%RU64 cMsDelta=%RU64\n", 2055 cbWritten, pStreamDS->Out.tsLastTransferredMs, msPrev ? pStreamDS->Out.tsLastTransferredMs - msPrev : 0)); 2056 } 2057 return VINF_SUCCESS; 2058 } 2059 2060 static int dsoundDestroyStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 2061 { 2062 LogFlowFuncEnter(); 2063 2064 HRESULT hr = directSoundPlayStop(pThis, pStreamDS, true /* fFlush */); 2035 hr = E_UNEXPECTED; 2036 2037 LogFlowFunc(("Returning %Rhrc\n", hr)); 2038 return hr; 2039 } 2040 2041 2042 static HRESULT directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush) 2043 { 2044 AssertPtrReturn(pThis, E_POINTER); 2045 AssertPtrReturn(pStreamDS, E_POINTER); 2046 2047 RT_NOREF(pThis); 2048 2049 HRESULT hr = S_OK; 2050 2051 if (pStreamDS->In.pDSCB) 2052 { 2053 DSLOG(("DSound: Stopping capture\n")); 2054 hr = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB); 2055 } 2056 2065 2057 if (SUCCEEDED(hr)) 2066 2058 { 2067 hr = directSoundPlayClose(pThis, pStreamDS); 2068 if (FAILED(hr)) 2069 return VERR_GENERAL_FAILURE; /** @todo Fix. */ 2070 } 2071 2072 return VINF_SUCCESS; 2073 } 2074 2075 static int dsoundCreateStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, 2076 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 2077 { 2078 LogFunc(("pStreamDS=%p, pCfgReq=%p, enmRecSource=%s\n", 2079 pStreamDS, pCfgReq, PDMAudioRecSrcGetName(pCfgReq->u.enmSrc))); 2080 2081 2082 /* Try to open capture in case the device is already there. */ 2083 int rc; 2084 HRESULT hr = directSoundCaptureOpen(pThis, pStreamDS, pCfgReq, pCfgAcq); 2085 if (SUCCEEDED(hr)) 2086 { 2087 rc = PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq); 2088 if (RT_SUCCESS(rc)) 2089 dsoundStreamReset(pThis, pStreamDS); 2090 } 2091 else 2092 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE; 2093 2094 return rc; 2095 } 2059 if (fFlush) 2060 dsoundStreamReset(pThis, pStreamDS); 2061 } 2062 2063 if (FAILED(hr)) 2064 DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr)); 2065 2066 return hr; 2067 } 2068 2096 2069 2097 2070 static int dsoundControlStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PDMAUDIOSTREAMCMD enmStreamCmd) … … 2160 2133 } 2161 2134 2135 2136 /** 2137 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl} 2138 */ 2139 static DECLCALLBACK(int) drvHostDSoundHA_StreamControl(PPDMIHOSTAUDIO pInterface, 2140 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd) 2141 { 2142 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2143 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 2144 2145 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface); 2146 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2147 2148 int rc; 2149 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN) 2150 rc = dsoundControlStreamIn(pThis, pStreamDS, enmStreamCmd); 2151 else 2152 rc = dsoundControlStreamOut(pThis, pStreamDS, enmStreamCmd); 2153 2154 return rc; 2155 } 2156 2157 2158 /** 2159 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable} 2160 */ 2161 static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 2162 { 2163 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); */ RT_NOREF(pInterface); 2164 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2165 AssertPtrReturn(pStreamDS, 0); 2166 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN); 2167 2168 if (pStreamDS->fEnabled) 2169 { 2170 /* This is the same calculation as for StreamGetPending. */ 2171 AssertPtr(pStreamDS->In.pDSCB); 2172 DWORD offCaptureCursor = 0; 2173 DWORD offReadCursor = 0; 2174 HRESULT hrc = IDirectSoundCaptureBuffer_GetCurrentPosition(pStreamDS->In.pDSCB, &offCaptureCursor, &offReadCursor); 2175 if (SUCCEEDED(hrc)) 2176 { 2177 uint32_t cbPending = dsoundRingDistance(offCaptureCursor, offReadCursor, pStreamDS->cbBufSize); 2178 Log3Func(("cbPending=%RU32\n", cbPending)); 2179 return cbPending; 2180 } 2181 AssertMsgFailed(("hrc=%Rhrc\n", hrc)); 2182 } 2183 2184 return 0; 2185 } 2186 2187 2188 /** 2189 * Retrieves the number of free bytes available for writing to a DirectSound output stream. 2190 * 2191 * @return VBox status code. VERR_NOT_AVAILABLE if unable to determine or the 2192 * buffer was not recoverable. 2193 * @param pThis Host audio driver instance. 2194 * @param pStreamDS DirectSound output stream to retrieve number for. 2195 * @param pdwFree Where to return the free amount on success. 2196 * @param poffPlayCursor Where to return the play cursor offset. 2197 */ 2198 static int dsoundGetFreeOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, DWORD *pdwFree, DWORD *poffPlayCursor) 2199 { 2200 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 2201 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER); 2202 AssertPtrReturn(pdwFree, VERR_INVALID_POINTER); 2203 AssertPtr(poffPlayCursor); 2204 2205 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT); /* Paranoia. */ 2206 2207 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB; 2208 if (!pDSB) 2209 { 2210 AssertPtr(pDSB); 2211 return VERR_INVALID_POINTER; 2212 } 2213 2214 HRESULT hr = S_OK; 2215 2216 /* Get the current play position which is used for calculating the free space in the buffer. */ 2217 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++) 2218 { 2219 DWORD offPlayCursor = 0; 2220 DWORD offWriteCursor = 0; 2221 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &offPlayCursor, &offWriteCursor); 2222 if (SUCCEEDED(hr)) 2223 { 2224 int32_t cbDiff = offWriteCursor - offPlayCursor; 2225 if (cbDiff < 0) 2226 cbDiff += pStreamDS->cbBufSize; 2227 2228 int32_t cbFree = offPlayCursor - pStreamDS->Out.offWritePos; 2229 if (cbFree < 0) 2230 cbFree += pStreamDS->cbBufSize; 2231 2232 if (cbFree > (int32_t)pStreamDS->cbBufSize - cbDiff) 2233 { 2234 /** @todo count/log these. */ 2235 pStreamDS->Out.offWritePos = offWriteCursor; 2236 cbFree = pStreamDS->cbBufSize - cbDiff; 2237 } 2238 2239 /* When starting to use a DirectSound buffer, offPlayCursor and offWriteCursor 2240 * both point at position 0, so we won't be able to detect how many bytes 2241 * are writable that way. 2242 * 2243 * So use our per-stream written indicator to see if we just started a stream. */ 2244 if (pStreamDS->Out.cbWritten == 0) 2245 cbFree = pStreamDS->cbBufSize; 2246 2247 DSLOGREL(("DSound: offPlayCursor=%RU32, offWriteCursor=%RU32, offWritePos=%RU32 -> cbFree=%RI32\n", 2248 offPlayCursor, offWriteCursor, pStreamDS->Out.offWritePos, cbFree)); 2249 2250 *pdwFree = cbFree; 2251 *poffPlayCursor = offPlayCursor; 2252 return VINF_SUCCESS; 2253 } 2254 2255 if (hr != DSERR_BUFFERLOST) /** @todo MSDN doesn't state this error for GetCurrentPosition(). */ 2256 break; 2257 2258 LogFunc(("Getting playing position failed due to lost buffer, restoring ...\n")); 2259 2260 directSoundPlayRestore(pThis, pDSB); 2261 } 2262 2263 if (hr != DSERR_BUFFERLOST) /* Avoid log flooding if the error is still there. */ 2264 DSLOGREL(("DSound: Getting current playback position failed with %Rhrc\n", hr)); 2265 2266 LogFunc(("Failed with %Rhrc\n", hr)); 2267 2268 *poffPlayCursor = pStreamDS->cbBufSize; 2269 return VERR_NOT_AVAILABLE; 2270 } 2271 2272 2273 /** 2274 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable} 2275 */ 2276 static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 2277 { 2278 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); 2279 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2280 AssertPtrReturn(pStreamDS, 0); 2281 2282 DWORD cbFree = 0; 2283 DWORD offIgn = 0; 2284 int rc = dsoundGetFreeOut(pThis, pStreamDS, &cbFree, &offIgn); 2285 AssertRCReturn(rc, 0); 2286 2287 return cbFree; 2288 } 2289 2290 #if 0 /* This isn't working as the write cursor is more a function of time than what we do. 2291 Previously we only reported the pre-buffering status anyway, so no harm. */ 2292 /** 2293 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending} 2294 */ 2295 static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 2296 { 2297 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); */ RT_NOREF(pInterface); 2298 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2299 AssertPtrReturn(pStreamDS, 0); 2300 2301 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT) 2302 { 2303 /* This is a similar calculation as for StreamGetReadable, only for an output buffer. */ 2304 AssertPtr(pStreamDS->In.pDSCB); 2305 DWORD offPlayCursor = 0; 2306 DWORD offWriteCursor = 0; 2307 HRESULT hrc = IDirectSoundBuffer8_GetCurrentPosition(pStreamDS->Out.pDSB, &offPlayCursor, &offWriteCursor); 2308 if (SUCCEEDED(hrc)) 2309 { 2310 uint32_t cbPending = dsoundRingDistance(offWriteCursor, offPlayCursor, pStreamDS->cbBufSize); 2311 Log3Func(("cbPending=%RU32\n", cbPending)); 2312 return cbPending; 2313 } 2314 AssertMsgFailed(("hrc=%Rhrc\n", hrc)); 2315 } 2316 /* else: For input streams we never have any pending data. */ 2317 2318 return 0; 2319 } 2320 #endif 2321 2322 /** 2323 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus} 2324 */ 2325 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostDSoundHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 2326 { 2327 RT_NOREF(pInterface); 2328 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAGS_NONE); 2329 2330 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2331 2332 PDMAUDIOSTREAMSTS fStrmStatus = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED; 2333 2334 if (pStreamDS->fEnabled) 2335 fStrmStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED; 2336 2337 return fStrmStatus; 2338 } 2339 2340 2341 /** 2342 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay} 2343 */ 2344 static DECLCALLBACK(int) drvHostDSoundHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 2345 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 2346 { 2347 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); 2348 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2349 AssertPtrReturn(pStreamDS, 0); 2350 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 2351 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 2352 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); 2353 2354 if (pStreamDS->fEnabled) 2355 AssertReturn(pStreamDS->cbBufSize, VERR_INTERNAL_ERROR_2); 2356 else 2357 { 2358 Log2Func(("Stream disabled, skipping\n")); 2359 return VINF_SUCCESS; 2360 } 2361 2362 /** @todo Any condition under which we should call dsoundUpdateStatusInternal(pThis) here? 2363 * The old code thought it did so in case of failure, only it couldn't ever fails, so it never did. */ 2364 2365 /* 2366 * Transfer loop. 2367 */ 2368 uint32_t cbWritten = 0; 2369 while (cbBuf > 0) 2370 { 2371 /* 2372 * Figure out how much we can possibly write. 2373 */ 2374 DWORD offPlayCursor = 0; 2375 DWORD cbWritable = 0; 2376 int rc = dsoundGetFreeOut(pThis, pStreamDS, &cbWritable, &offPlayCursor); 2377 AssertRCReturn(rc, rc); 2378 if (cbWritable < pStreamDS->Cfg.Props.cbFrame) 2379 break; 2380 2381 uint32_t const cbToWrite = RT_MIN(cbWritable, cbBuf); 2382 Log3Func(("offPlay=%#x offWritePos=%#x -> cbWritable=%#x cbToWrite=%#x%s%s\n", offPlayCursor, pStreamDS->Out.offWritePos, 2383 cbWritable, cbToWrite, pStreamDS->Out.fFirstTransfer ? " first" : "", pStreamDS->Out.fDrain ? " drain" : "")); 2384 2385 /* 2386 * Lock that amount of buffer. 2387 */ 2388 PVOID pv1 = NULL; 2389 DWORD cb1 = 0; 2390 PVOID pv2 = NULL; 2391 DWORD cb2 = 0; 2392 HRESULT hrc = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, cbToWrite, 2393 &pv1, &pv2, &cb1, &cb2, 0 /*dwFlags*/); 2394 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */ 2395 //AssertMsg(cb1 + cb2 == cbToWrite, ("%#x + %#x vs %#x\n", cb1, cb2, cbToWrite)); 2396 2397 /* 2398 * Copy over the data. 2399 */ 2400 memcpy(pv1, pvBuf, cb1); 2401 pvBuf = (uint8_t *)pvBuf + cb1; 2402 cbBuf -= cb1; 2403 cbWritten += cb1; 2404 2405 if (pv2) 2406 { 2407 memcpy(pv2, pvBuf, cb2); 2408 pvBuf = (uint8_t *)pvBuf + cb2; 2409 cbBuf -= cb2; 2410 cbWritten += cb2; 2411 } 2412 2413 /* 2414 * Unlock and update the write position. 2415 */ 2416 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2); /** @todo r=bird: pThis + pDSB parameters here for Unlock, but only pThis for Lock. Why? */ 2417 pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cb1 + cb2) % pStreamDS->cbBufSize; 2418 2419 /* 2420 * If this was the first chunk, kick off playing. 2421 */ 2422 if (!pStreamDS->Out.fFirstTransfer) 2423 { /* likely */ } 2424 else 2425 { 2426 *pcbWritten = cbWritten; 2427 hrc = directSoundPlayStart(pThis, pStreamDS); 2428 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */ 2429 pStreamDS->Out.fFirstTransfer = false; 2430 } 2431 } 2432 2433 /* 2434 * Done. 2435 */ 2436 *pcbWritten = cbWritten; 2437 2438 pStreamDS->Out.cbTransferred += cbWritten; 2439 if (cbWritten) 2440 { 2441 uint64_t const msPrev = pStreamDS->Out.tsLastTransferredMs; 2442 pStreamDS->Out.cbLastTransferred = cbWritten; 2443 pStreamDS->Out.tsLastTransferredMs = RTTimeMilliTS(); 2444 LogFlowFunc(("cbLastTransferred=%RU32, tsLastTransferredMs=%RU64 cMsDelta=%RU64\n", 2445 cbWritten, pStreamDS->Out.tsLastTransferredMs, msPrev ? pStreamDS->Out.tsLastTransferredMs - msPrev : 0)); 2446 } 2447 return VINF_SUCCESS; 2448 } 2449 2450 2162 2451 /** 2163 2452 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture} … … 2275 2564 } 2276 2565 2277 static int dsoundDestroyStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 2278 { 2566 2567 /********************************************************************************************************************************* 2568 * PDMDRVINS::IBase Interface * 2569 *********************************************************************************************************************************/ 2570 2571 /** 2572 * @callback_method_impl{PDMIBASE,pfnQueryInterface} 2573 */ 2574 static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID) 2575 { 2576 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); 2577 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND); 2578 2579 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); 2580 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio); 2581 return NULL; 2582 } 2583 2584 2585 /********************************************************************************************************************************* 2586 * PDMDRVREG Interface * 2587 *********************************************************************************************************************************/ 2588 2589 /** 2590 * @callback_method_impl{FNPDMDRVDESTRUCT, pfnDestruct} 2591 */ 2592 static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns) 2593 { 2594 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND); 2595 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); 2596 2279 2597 LogFlowFuncEnter(); 2280 2598 2281 directSoundCaptureClose(pThis, pStreamDS); 2282 2283 return VINF_SUCCESS; 2284 } 2285 2286 /** 2287 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig} 2288 */ 2289 static DECLCALLBACK(int) drvHostDSoundHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg) 2290 { 2291 RT_NOREF(pInterface); 2292 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2293 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER); 2294 2295 RT_BZERO(pBackendCfg, sizeof(PPDMAUDIOBACKENDCFG)); 2296 2297 pBackendCfg->cbStreamOut = sizeof(DSOUNDSTREAM); 2298 pBackendCfg->cbStreamIn = sizeof(DSOUNDSTREAM); 2299 2300 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "DirectSound"); 2301 2302 pBackendCfg->cMaxStreamsIn = UINT32_MAX; 2303 pBackendCfg->cMaxStreamsOut = UINT32_MAX; 2304 2305 return VINF_SUCCESS; 2306 } 2307 2308 /** 2309 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetDevices} 2310 */ 2311 static DECLCALLBACK(int) drvHostDSoundHA_GetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHOSTENUM pDeviceEnum) 2312 { 2313 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2314 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER); 2315 2316 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface); 2317 2318 int rc = RTCritSectEnter(&pThis->CritSect); 2319 if (RT_SUCCESS(rc)) 2320 { 2321 PDMAudioHostEnumInit(pDeviceEnum); 2322 rc = dsoundDevicesEnumerate(pThis, pDeviceEnum); 2323 if (RT_FAILURE(rc)) 2324 PDMAudioHostEnumDelete(pDeviceEnum); 2325 2326 int rc2 = RTCritSectLeave(&pThis->CritSect); 2327 AssertRC(rc2); 2328 } 2329 2330 LogFlowFunc(("Returning %Rrc\n", rc)); 2331 return rc; 2599 #ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT 2600 if (pThis->m_pNotificationClient) 2601 { 2602 pThis->m_pNotificationClient->Unregister(); 2603 pThis->m_pNotificationClient->Release(); 2604 2605 pThis->m_pNotificationClient = NULL; 2606 } 2607 #endif 2608 2609 PDMAudioHostEnumDelete(&pThis->DeviceEnum); 2610 2611 if (pThis->pDrvIns) 2612 CoUninitialize(); 2613 2614 int rc2 = RTCritSectDelete(&pThis->CritSect); 2615 AssertRC(rc2); 2616 2617 LogFlowFuncLeave(); 2332 2618 } 2333 2619 … … 2353 2639 } 2354 2640 2641 2355 2642 static void dsoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg) 2356 2643 { … … 2363 2650 } 2364 2651 2365 /**2366 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}2367 */2368 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDSoundHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)2369 {2370 RT_NOREF(enmDir);2371 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);2372 2373 return PDMAUDIOBACKENDSTS_RUNNING;2374 }2375 2376 /**2377 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}2378 */2379 static DECLCALLBACK(int) drvHostDSoundHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,2380 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)2381 {2382 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);2383 AssertPtrReturn(pStream, VERR_INVALID_POINTER);2384 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);2385 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);2386 2387 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);2388 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;2389 2390 int rc;2391 if (pCfgReq->enmDir == PDMAUDIODIR_IN)2392 rc = dsoundCreateStreamIn(pThis, pStreamDS, pCfgReq, pCfgAcq);2393 else2394 rc = dsoundCreateStreamOut(pThis, pStreamDS, pCfgReq, pCfgAcq);2395 2396 if (RT_SUCCESS(rc))2397 {2398 /** @todo already copied */2399 rc = PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq);2400 if (RT_SUCCESS(rc))2401 rc = RTCritSectInit(&pStreamDS->CritSect);2402 }2403 2404 return rc;2405 }2406 2407 /**2408 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}2409 */2410 static DECLCALLBACK(int) drvHostDSoundHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)2411 {2412 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);2413 AssertPtrReturn(pStream, VERR_INVALID_POINTER);2414 2415 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);2416 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;2417 2418 int rc;2419 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)2420 rc = dsoundDestroyStreamIn(pThis, pStreamDS);2421 else2422 rc = dsoundDestroyStreamOut(pThis, pStreamDS);2423 2424 if (RT_SUCCESS(rc))2425 {2426 if (RTCritSectIsInitialized(&pStreamDS->CritSect))2427 rc = RTCritSectDelete(&pStreamDS->CritSect);2428 }2429 2430 return rc;2431 }2432 2433 /**2434 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}2435 */2436 static DECLCALLBACK(int) drvHostDSoundHA_StreamControl(PPDMIHOSTAUDIO pInterface,2437 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)2438 {2439 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);2440 AssertPtrReturn(pStream, VERR_INVALID_POINTER);2441 2442 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);2443 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;2444 2445 int rc;2446 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)2447 rc = dsoundControlStreamIn(pThis, pStreamDS, enmStreamCmd);2448 else2449 rc = dsoundControlStreamOut(pThis, pStreamDS, enmStreamCmd);2450 2451 return rc;2452 }2453 2454 /**2455 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}2456 */2457 static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)2458 {2459 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); */ RT_NOREF(pInterface);2460 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;2461 AssertPtrReturn(pStreamDS, 0);2462 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN);2463 2464 if (pStreamDS->fEnabled)2465 {2466 /* This is the same calculation as for StreamGetPending. */2467 AssertPtr(pStreamDS->In.pDSCB);2468 DWORD offCaptureCursor = 0;2469 DWORD offReadCursor = 0;2470 HRESULT hrc = IDirectSoundCaptureBuffer_GetCurrentPosition(pStreamDS->In.pDSCB, &offCaptureCursor, &offReadCursor);2471 if (SUCCEEDED(hrc))2472 {2473 uint32_t cbPending = dsoundRingDistance(offCaptureCursor, offReadCursor, pStreamDS->cbBufSize);2474 Log3Func(("cbPending=%RU32\n", cbPending));2475 return cbPending;2476 }2477 AssertMsgFailed(("hrc=%Rhrc\n", hrc));2478 }2479 2480 return 0;2481 }2482 2483 /**2484 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}2485 */2486 static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)2487 {2488 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);2489 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;2490 AssertPtrReturn(pStreamDS, 0);2491 2492 DWORD cbFree = 0;2493 DWORD offIgn = 0;2494 int rc = dsoundGetFreeOut(pThis, pStreamDS, &cbFree, &offIgn);2495 AssertRCReturn(rc, 0);2496 2497 return cbFree;2498 }2499 2500 #if 0 /* This isn't working as the write cursor is more a function of time than what we do.2501 Previously we only reported the pre-buffering status anyway, so no harm. */2502 /**2503 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}2504 */2505 static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)2506 {2507 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); */ RT_NOREF(pInterface);2508 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;2509 AssertPtrReturn(pStreamDS, 0);2510 2511 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)2512 {2513 /* This is a similar calculation as for StreamGetReadable, only for an output buffer. */2514 AssertPtr(pStreamDS->In.pDSCB);2515 DWORD offPlayCursor = 0;2516 DWORD offWriteCursor = 0;2517 HRESULT hrc = IDirectSoundBuffer8_GetCurrentPosition(pStreamDS->Out.pDSB, &offPlayCursor, &offWriteCursor);2518 if (SUCCEEDED(hrc))2519 {2520 uint32_t cbPending = dsoundRingDistance(offWriteCursor, offPlayCursor, pStreamDS->cbBufSize);2521 Log3Func(("cbPending=%RU32\n", cbPending));2522 return cbPending;2523 }2524 AssertMsgFailed(("hrc=%Rhrc\n", hrc));2525 }2526 /* else: For input streams we never have any pending data. */2527 2528 return 0;2529 }2530 #endif2531 2532 /**2533 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}2534 */2535 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostDSoundHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)2536 {2537 RT_NOREF(pInterface);2538 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAGS_NONE);2539 2540 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;2541 2542 PDMAUDIOSTREAMSTS fStrmStatus = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;2543 2544 if (pStreamDS->fEnabled)2545 fStrmStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;2546 2547 return fStrmStatus;2548 }2549 2550 2551 /*********************************************************************************************************************************2552 * PDMDRVINS::IBase Interface *2553 *********************************************************************************************************************************/2554 2555 /**2556 * @callback_method_impl{PDMIBASE,pfnQueryInterface}2557 */2558 static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)2559 {2560 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);2561 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);2562 2563 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);2564 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);2565 return NULL;2566 }2567 2568 2569 /*********************************************************************************************************************************2570 * PDMDRVREG Interface *2571 *********************************************************************************************************************************/2572 2573 /**2574 * @callback_method_impl{FNPDMDRVDESTRUCT, pfnDestruct}2575 */2576 static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)2577 {2578 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);2579 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);2580 2581 LogFlowFuncEnter();2582 2583 #ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT2584 if (pThis->m_pNotificationClient)2585 {2586 pThis->m_pNotificationClient->Unregister();2587 pThis->m_pNotificationClient->Release();2588 2589 pThis->m_pNotificationClient = NULL;2590 }2591 #endif2592 2593 PDMAudioHostEnumDelete(&pThis->DeviceEnum);2594 2595 if (pThis->pDrvIns)2596 CoUninitialize();2597 2598 int rc2 = RTCritSectDelete(&pThis->CritSect);2599 AssertRC(rc2);2600 2601 LogFlowFuncLeave();2602 }2603 2652 2604 2653 /** … … 2621 2670 /* IHostAudio */ 2622 2671 pThis->IHostAudio.pfnGetConfig = drvHostDSoundHA_GetConfig; 2672 pThis->IHostAudio.pfnGetDevices = drvHostDSoundHA_GetDevices; 2623 2673 pThis->IHostAudio.pfnGetStatus = drvHostDSoundHA_GetStatus; 2624 2674 pThis->IHostAudio.pfnStreamCreate = drvHostDSoundHA_StreamCreate; … … 2627 2677 pThis->IHostAudio.pfnStreamGetReadable = drvHostDSoundHA_StreamGetReadable; 2628 2678 pThis->IHostAudio.pfnStreamGetWritable = drvHostDSoundHA_StreamGetWritable; 2679 pThis->IHostAudio.pfnStreamGetPending = NULL; 2629 2680 pThis->IHostAudio.pfnStreamGetStatus = drvHostDSoundHA_StreamGetStatus; 2630 2681 pThis->IHostAudio.pfnStreamPlay = drvHostDSoundHA_StreamPlay; 2631 2682 pThis->IHostAudio.pfnStreamCapture = drvHostDSoundHA_StreamCapture; 2632 pThis->IHostAudio.pfnGetDevices = drvHostDSoundHA_GetDevices;2633 pThis->IHostAudio.pfnStreamGetPending = NULL;2634 2683 2635 2684 /*
Note:
See TracChangeset
for help on using the changeset viewer.