Changeset 73370 in vbox
- Timestamp:
- Jul 26, 2018 1:52:12 PM (7 years ago)
- svn:sync-xref-src-repo-rev:
- 123987
- Location:
- trunk
- Files:
-
- 21 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/VBox/vmm/pdmaudioifs.h
r73242 r73370 4 4 5 5 /* 6 * Copyright (C) 2006-201 7Oracle Corporation6 * Copyright (C) 2006-2018 Oracle Corporation 7 7 * 8 8 * This file is part of VirtualBox Open Source Edition (OSE), as … … 82 82 * A PDMAUDIOFRAME is the internal representation of a single audio frame, which consists of a single left 83 83 * and right audio sample in time. Only mono (1) and stereo (2) channel(s) currently are supported. 84 * 85 * 86 * == Timing 87 * 88 * Handling audio data in a virtual environment is hard, as the human perception is very sensitive 89 * to the slightest cracks and stutters in the audible data. This can happen if the VM's timing is 90 * lagging behind or not within the expected time frame. 91 * 92 * The two main components which unfortunately contradict each other is a) the audio device emulation 93 * and b) the audio backend(s) on the host. Those need to be served in a timely manner to function correctly. 94 * To make e.g. the device emulation rely on the pace the host backend(s) set - or vice versa - will not work, 95 * as the guest's audio system / drivers then will not be able to compensate this accordingly. 96 * 97 * So each component, the device emulation, the audio connector(s) and the backend(s) must do its thing 98 * *when* it needs to do it, independently of the others. For that we use various (small) ring buffers to 99 * (hopefully) serve all components with the amount of data *when* they need it. 100 * 101 * Additionally, the device emulation can run with a different audio frame size, while the backends(s) may 102 * require a different frame size (16 bit stereo -> 8 bit mono, for example). 103 * 104 * The device emulation can give the audio connector(s) a scheduling hint (optional), e.g. in which interval 105 * it expects any data processing. 106 * 107 * A data transfer for playing audio data from the guest on the host looks like this: 108 * (RB = Ring Buffer, MB = Mixing Buffer) 109 * 110 * (A) Device DMA -> (B) Device RB -> (C) Audio Connector Guest MB -> (D) Audio Connector Host MB -> \ 111 * (E) Backend RB (optional, up to the backend) > (F) Backend audio framework 112 * 113 * For capturing audio data the above chain is similar, just in a different direction, of course. 114 * 115 * The audio connector hereby plays a key role when it comes to (pre-) buffering data to minimize any audio stutters 116 * and/or cracks. The following values, which also can be tweaked via CFGM / extra-data are available: 117 * 118 * - The pre-buffering time (in ms): Audio data which needs to be buffered before any playback (or capturing) can happen. 119 * - The actual buffer size (in ms): How big the mixing buffer (for C and D) will be. 120 * - The period size (in ms): How big a chunk of audio (often called period or fragment) for F must be to get handled correctly. 121 * 122 * The above values can be set on a per-driver level, whereas input and output streams for a driver also can be handled 123 * set independently. The verbose audio (release) log will tell about the (final) state of each audio stream. 84 124 * 85 125 * … … 535 575 * The audio data will get handled as PDMAUDIOFRAME frames without any modification done. */ 536 576 PDMAUDIOSTREAMLAYOUT enmLayout; 537 /** Hint about the optimal frame buffer size (in audio frames). 538 * 0 if no hint is given. */ 539 uint32_t cFrameBufferHint; 577 /** Device emulation-specific data needed for the audio connector. */ 540 578 struct 541 579 { 542 /** Scheduling hint given from the device emulation about when this stream is being served on average.580 /** Scheduling hint set by the device emulation about when this stream is being served on average (in ms). 543 581 * Can be 0 if not hint given or some other mechanism (e.g. callbacks) is being used. */ 544 582 uint32_t uSchedulingHintMs; … … 546 584 /** 547 585 * Backend-specific data for the stream. 586 * On input (requested configuration) those values are set by the audio connector to let the backend know what we expect. 587 * On output (acquired configuration) those values reflect the values set and used by the backend. 548 588 * Set by the backend on return. Not all backends support all values / features. 549 589 */ … … 999 1039 PDMAUDIOSTREAMCTX enmCtx; 1000 1040 /** Timestamp (in ms) since last iteration. */ 1001 uint64_t tsLastIterateMS; 1041 uint64_t tsLastIteratedMs; 1042 /** Timestamp (in ms) since last playback / capture. */ 1043 uint64_t tsLastPlayedCapturedMs; 1044 /** Timestamp (in ms) since last read (input streams) or 1045 * write (output streams). */ 1046 uint64_t tsLastReadWrittenMs; 1047 /** For output streams this indicates whether the stream has reached 1048 * its playback threshold, e.g. is playing audio. 1049 * For input streams this indicates whether the stream has enough input 1050 * data to actually start reading audio. */ 1051 bool fThresholdReached; 1002 1052 /** Union for input/output specifics. */ 1003 1053 union -
trunk/src/VBox/Devices/Audio/AudioMixer.cpp
r73342 r73370 853 853 uint32_t cbReadable = 0; 854 854 855 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING) 856 { 855 857 #ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF 856 858 # error "Implement me!" 857 859 #else 858 /* The hosts sets the pace -- 859 * so we try to find the maximum of readable data of all connected streams to this sink. */ 860 PAUDMIXSTREAM pMixStream; 861 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 862 { 863 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) 864 { 865 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName)); 866 continue; 867 } 868 869 cbReadable = RT_MAX(cbReadable, 870 pMixStream->pConn->pfnStreamGetReadable(pMixStream->pConn, pMixStream->pStream)); 871 872 break; /** @todo For now we only support recording by the first stream added. */ 873 } 860 /* Return how much data we can deliver since the last read. */ 861 cbReadable = DrvAudioHlpMsToBytes(&pSink->PCMProps, RTTimeMilliTS() - pSink->tsLastReadWrittenMs); 874 862 #endif 863 } 875 864 876 865 Log3Func(("[%s] cbReadable=%RU32\n", pSink->pszName, cbReadable)); … … 899 888 return 0; 900 889 901 uint32_t cbWritable = UINT32_MAX;890 uint32_t cbWritable = 0; 902 891 903 892 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING) … … 907 896 # error "Implement me!" 908 897 #else 909 /* The hosts sets the pace -- 910 * so we try to find the minimum of writable data to all connected streams to this sink. */ 911 PAUDMIXSTREAM pMixStream; 912 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 913 { 914 const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream); 915 916 Log3Func(("[%s] Stream '%s' cbWritableStream=%RU32\n", pSink->pszName, pMixStream->pszName, cbWritableStream)); 917 918 if (cbWritableStream < cbWritable) 919 cbWritable = cbWritableStream; 920 } 898 /* Return how much data we expect since the last write. */ 899 cbWritable = DrvAudioHlpMsToBytes(&pSink->PCMProps, RTTimeMilliTS() - pSink->tsLastReadWrittenMs); 921 900 #endif 922 901 } 923 924 if (cbWritable == UINT32_MAX)925 cbWritable = 0;926 902 927 903 Log3Func(("[%s] cbWritable=%RU32\n", pSink->pszName, cbWritable)); … … 1167 1143 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY; 1168 1144 1145 /* Update our last read time stamp. */ 1146 pSink->tsLastReadWrittenMs = RTTimeMilliTS(); 1147 1169 1148 #ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF 1170 1149 # error "Implement me!" … … 1304 1283 1305 1284 /* Update last updated timestamp. */ 1306 pSink->tsLastUpdatedM S= RTTimeMilliTS();1285 pSink->tsLastUpdatedMs = RTTimeMilliTS(); 1307 1286 1308 1287 /* Reset status. */ … … 1581 1560 1582 1561 /* Update last updated timestamp. */ 1583 pSink->tsLastUpdatedM S= RTTimeMilliTS();1562 pSink->tsLastUpdatedMs = RTTimeMilliTS(); 1584 1563 1585 1564 /* All streams disabled and the sink is in pending disable mode? */ … … 1758 1737 } 1759 1738 1739 /* Update our last written time stamp. */ 1740 pSink->tsLastReadWrittenMs = RTTimeMilliTS(); 1741 1760 1742 if (pcbWritten) 1761 1743 *pcbWritten = cbBuf; /* Always report everything written, as the backends need to keep up themselves. */ -
trunk/src/VBox/Devices/Audio/AudioMixer.h
r73356 r73370 196 196 /** The volume of this sink, combined with the last set master volume. */ 197 197 PDMAUDIOVOLUME VolumeCombined; 198 /** Timestamp (in ms) since last update. */ 199 uint64_t tsLastUpdatedMS; 198 /** Timestamp since last update (in ms). */ 199 uint64_t tsLastUpdatedMs; 200 /** Last read (recording) / written (playback) timestamp (in ms). */ 201 uint64_t tsLastReadWrittenMs; 200 202 #ifdef VBOX_AUDIO_MIXER_DEBUG 201 203 struct -
trunk/src/VBox/Devices/Audio/DevIchAc97.cpp
r73242 r73370 313 313 /** The stream's current configuration. */ 314 314 PDMAUDIOSTREAMCFG Cfg; //+104 315 uint32_t Padding2; 315 316 #ifdef VBOX_WITH_AUDIO_AC97_ASYNC_IO 316 317 /** Asynchronous I/O state members. */ -
trunk/src/VBox/Devices/Audio/DevSB16.cpp
r73241 r73370 1605 1605 pThis->left_till_irq = pThis->block_size; 1606 1606 1607 uint32_t cbOutMin = UINT32_MAX; 1608 1609 PSB16DRIVER pDrv; 1610 RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node) 1611 { 1612 if (!pDrv->Out.pStream) 1613 continue; 1614 1615 uint32_t cbOut = pDrv->pConnector->pfnStreamGetWritable(pDrv->pConnector, pDrv->Out.pStream); 1616 1617 if (cbOut < cbOutMin) 1618 cbOutMin = cbOut; 1619 } 1620 1621 LogFlowFunc(("cbOutMin=%RU32\n", cbOutMin)); 1622 if (cbOutMin == UINT32_MAX) 1623 { 1624 free = dma_len; 1625 } 1626 else 1627 { 1628 free = cbOutMin & ~pThis->align; /** @todo int vs. uint32. */ 1629 if ((free <= 0) || !dma_len) 1630 return dma_pos; 1631 } 1607 free = dma_len; 1632 1608 1633 1609 copy = free; -
trunk/src/VBox/Devices/Audio/DrvAudio.cpp
r73230 r73370 560 560 } 561 561 562 case PDMAUDIOSTREAMCMD_DRAIN: 563 { 564 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream->pvBackend, PDMAUDIOSTREAMCMD_DRAIN); 565 break; 566 } 567 562 568 default: 563 569 { … … 570 576 if (RT_FAILURE(rc)) 571 577 { 572 LogRel(("Audio: %s stream '%s' failed with %Rrc\n", DrvAudioHlpStreamCmdToStr(enmStreamCmd), pHstStream->szName, rc)); 578 if ( rc != VERR_NOT_IMPLEMENTED 579 && rc != VERR_NOT_SUPPORTED) 580 LogRel(("Audio: %s stream '%s' failed with %Rrc\n", DrvAudioHlpStreamCmdToStr(enmStreamCmd), pHstStream->szName, rc)); 581 573 582 LogFunc(("[%s] %s failed with %Rrc\n", pStream->szName, DrvAudioHlpStreamCmdToStr(enmStreamCmd), rc)); 574 583 } … … 598 607 AssertPtr(pGstStream); 599 608 609 /* pCfgHost->Backend.cfPreBuf = DrvAudioHlpMsToFrames(&pCfgHost->Props, 610 pCfgGuest->enmDir == PDMAUDIODIR_IN 611 ? pThis->In.msLatencyHost : pThis->Out.msLatencyHost);*/ 612 /* Latency can be 0 if not configured. */ 613 600 614 /* 601 615 * Init host stream. … … 606 620 607 621 #ifdef DEBUG 608 LogFunc(("[%s] Requested host format:\n", p Stream->szName));622 LogFunc(("[%s] Requested host format:\n", pHstStream->szName)); 609 623 DrvAudioHlpStreamCfgPrint(pCfgHost); 610 #else 611 LogRel2(("Audio: Creating stream '%s'\n", pStream->szName)); 612 LogRel2(("Audio: Requested %s host format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n", 613 pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName, 624 #endif 625 626 LogRel2(("Audio: Creating stream '%s'\n", pHstStream->szName)); 627 LogRel2(("Audio: Guest %s format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n", 628 pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pGstStream->szName, 629 pCfgGuest->Props.uHz, pCfgGuest->Props.cBits, pCfgGuest->Props.fSigned ? "S" : "U", 630 pCfgGuest->Props.cChannels, pCfgGuest->Props.cChannels == 1 ? "Channel" : "Channels")); 631 LogRel2(("Audio: Requested host %s format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n", 632 pCfgHost->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pHstStream->szName, 614 633 pCfgHost->Props.uHz, pCfgHost->Props.cBits, pCfgHost->Props.fSigned ? "S" : "U", 615 pCfgHost->Props.cChannels, pCfgHost->Props.cChannels == 0 ? "Channel" : "Channels")); 616 #endif 634 pCfgHost->Props.cChannels, pCfgHost->Props.cChannels == 1 ? "Channel" : "Channels")); 617 635 618 636 PDMAUDIOSTREAMCFG CfgHostAcq; … … 622 640 623 641 #ifdef DEBUG 624 LogFunc(("[%s] Acquired host format:\n", p Stream->szName));642 LogFunc(("[%s] Acquired host format:\n", pHstStream->szName)); 625 643 DrvAudioHlpStreamCfgPrint(&CfgHostAcq); 626 #else 627 LogRel2(("Audio: Acquired %s host format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n", 644 #endif 645 646 LogRel2(("Audio: Acquired host %s format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n", 628 647 CfgHostAcq.enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName, 629 648 CfgHostAcq.Props.uHz, CfgHostAcq.Props.cBits, CfgHostAcq.Props.fSigned ? "S" : "U", 630 CfgHostAcq.Props.cChannels, CfgHostAcq.Props.cChannels == 0 ? "Channel" : "Channels")); 631 #endif 632 633 /* No frame buffer size hint given by the backend? Default to some sane value. */ 634 if (!CfgHostAcq.cFrameBufferHint) 635 { 636 CfgHostAcq.cFrameBufferHint = _1K; /** @todo Make this configurable? */ 637 } 649 CfgHostAcq.Props.cChannels, CfgHostAcq.Props.cChannels == 1 ? "Channel" : "Channels")); 650 651 /* Let the user know if the backend changed some of the tweakable values. */ 652 if (CfgHostAcq.Backend.cfBufferSize != pCfgHost->Backend.cfBufferSize) 653 LogRel2(("Audio: Backend changed buffer size from %RU32 to %RU32 frames\n", 654 pCfgHost->Backend.cfBufferSize, CfgHostAcq.Backend.cfBufferSize)); 655 656 if (CfgHostAcq.Backend.cfPeriod != pCfgHost->Backend.cfPeriod) 657 LogRel2(("Audio: Backend changed period size from %RU32 to %RU32 frames\n", 658 pCfgHost->Backend.cfPeriod, CfgHostAcq.Backend.cfPeriod)); 659 660 if (CfgHostAcq.Backend.cfPreBuf != pCfgHost->Backend.cfPreBuf) 661 LogRel2(("Audio: Backend changed pre-buffering size from %RU32 to %RU32 frames\n", 662 pCfgHost->Backend.cfPreBuf, CfgHostAcq.Backend.cfPreBuf)); 663 664 /* 665 * Configure host buffers. 666 */ 667 668 /* If no own pre-buffer is set, let the backend choose. */ 669 uint32_t msPreBuf = DrvAudioHlpFramesToMs(&pCfgHost->Props, CfgHostAcq.Backend.cfPreBuf); 670 LogRel2(("Audio: Pre-buffering size of stream '%s' is %RU32ms (%RU32 frames)\n", 671 pHstStream->szName, msPreBuf, CfgHostAcq.Backend.cfPreBuf)); 672 673 /* Make sure the configured buffer size by the backend at least can hold the configured latency. */ 674 const uint32_t msPeriod = DrvAudioHlpFramesToMs(&pCfgHost->Props, CfgHostAcq.Backend.cfPeriod); 675 676 LogRel2(("Audio: Period size of stream '%s' is %RU32ms (%RU32 frames)\n", 677 pHstStream->szName, msPeriod, CfgHostAcq.Backend.cfPeriod)); 678 679 /* Check if the backend did return sane values and correct if necessary. */ 680 if (CfgHostAcq.Backend.cfBufferSize < CfgHostAcq.Backend.cfPeriod) 681 { 682 LogRel2(("Audio: Warning: Backend of stream '%s' did not set a valid buffer size (%RU32 frames), setting to %RU32 frames", 683 pHstStream->szName, CfgHostAcq.Backend.cfBufferSize, CfgHostAcq.Backend.cfPeriod)); 684 AssertFailed(); /* Should never happen. */ 685 } 686 687 uint32_t msBufferSize = DrvAudioHlpFramesToMs(&pCfgHost->Props, CfgHostAcq.Backend.cfBufferSize); 688 689 LogRel2(("Audio: Buffer size of stream '%s' is %RU32ms (%RU32 frames)\n", 690 pHstStream->szName, msBufferSize, CfgHostAcq.Backend.cfBufferSize)); 691 692 /* Set set host buffer size multiplicator. */ 693 const unsigned cHstBufferFactor = 2; /** @todo Make this configurable. */ 638 694 639 695 /* Destroy any former mixing buffer. */ … … 643 699 CfgHostAcq.Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(CfgHostAcq.Props.cBits, CfgHostAcq.Props.cChannels); 644 700 645 /* Set set host buffer size multiplicator. */646 const unsigned cFrameBufferHostFactor = 2; /** @todo Make this configurable. */647 648 LogFunc(("[%s] cFrames=%RU32 (x %u)\n", pHstStream->szName, CfgHostAcq.cFrameBufferHint, cFrameBufferHostFactor));649 650 701 int rc2 = AudioMixBufInit(&pHstStream->MixBuf, pHstStream->szName, &CfgHostAcq.Props, 651 CfgHostAcq. cFrameBufferHint * cFrameBufferHostFactor);702 CfgHostAcq.Backend.cfBufferSize * cHstBufferFactor); 652 703 AssertRC(rc2); 653 704 … … 660 711 */ 661 712 713 if (pCfgGuest->Device.uSchedulingHintMs) 714 LogRel2(("Audio: Stream '%s' got a scheduling hint of %RU32ms\n", pGstStream->szName, pCfgGuest->Device.uSchedulingHintMs)); 715 662 716 /* Destroy any former mixing buffer. */ 663 717 AudioMixBufDestroy(&pGstStream->MixBuf); 664 718 665 719 /* Set the guests's default audio data layout. */ 666 pCfg Host->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;720 pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; 667 721 668 722 /* Make sure to (re-)set the guest buffer's shift size. */ … … 670 724 671 725 /* Set set guest buffer size multiplicator. */ 672 const unsigned cFrameBufferGuestFactor = 10; /** @todo Make this configurable. */ 673 674 LogFunc(("[%s] cFrames=%RU32 (x %u)\n", pGstStream->szName, CfgHostAcq.cFrameBufferHint, cFrameBufferGuestFactor)); 726 const unsigned cGstBufferFactor = 4; /** @todo Make this configurable. */ 675 727 676 728 rc2 = AudioMixBufInit(&pGstStream->MixBuf, pGstStream->szName, &pCfgGuest->Props, 677 CfgHostAcq. cFrameBufferHint * cFrameBufferGuestFactor);729 CfgHostAcq.Backend.cfBufferSize * cGstBufferFactor); 678 730 AssertRC(rc2); 679 731 … … 938 990 } 939 991 940 const uint32_t cbFree = AudioMixBufFreeBytes(&pGstStream->MixBuf); 941 if (!cbFree) /* No free guest side buffer space, bail out. */ 942 { 943 AssertMsgFailed(("[%s] Stream full\n", pGstStream->szName)); 992 const uint32_t cbFree = AudioMixBufFreeBytes(&pHstStream->MixBuf); 993 if (cbFree < cbBuf) /* No space left on host side? Bail out. */ 994 LogRel2(("Audio: Lost audio frames (%RU32) due to full host stream '%s', expect stuttering audio output\n", 995 DrvAudioHlpBytesToFrames(&pHstStream->Cfg.Props, cbBuf - cbFree), pHstStream->szName)); 996 997 uint32_t cbToWrite = RT_MIN(cbBuf, cbFree); 998 if (cbToWrite > cbBuf) /* Paranoia. */ 999 cbToWrite = cbBuf; 1000 1001 if (!cbToWrite) 944 1002 break; 945 } 946 947 /* Do not attempt to write more than the guest side currently can handle. */ 948 if (cbBuf > cbFree) 949 cbBuf = cbFree; 1003 1004 uint64_t tsDeltaWrittenMs = RTTimeMilliTS() - pHstStream->tsLastReadWrittenMs; 1005 1006 Log3Func(("[%s] fThresholdReached=%RTbool, tsDeltaWrittenMs=%RU64\n", 1007 pHstStream->szName, pHstStream->fThresholdReached, tsDeltaWrittenMs)); 1008 1009 if ( pHstStream->fThresholdReached 1010 && tsDeltaWrittenMs < pGstStream->Cfg.Device.uSchedulingHintMs) 1011 { 1012 break; 1013 } 950 1014 951 1015 /* We use the guest side mixing buffer as an intermediate buffer to do some 952 1016 * (first) processing (if needed), so always write the incoming data at offset 0. */ 953 uint32_t cf Written = 0;954 rc = AudioMixBufWriteAt(&pGstStream->MixBuf, 0 /* offFrames */, pvBuf, cb Buf, &cfWritten);1017 uint32_t cfGstWritten = 0; 1018 rc = AudioMixBufWriteAt(&pGstStream->MixBuf, 0 /* offFrames */, pvBuf, cbToWrite, &cfGstWritten); 955 1019 if ( RT_FAILURE(rc) 956 || !cf Written)957 { 958 AssertMsgFailed(("[%s] Write failed: cb Buf=%RU32, cfWritten=%RU32, rc=%Rrc\n",959 pGstStream->szName, cb Buf, cfWritten, rc));1020 || !cfGstWritten) 1021 { 1022 AssertMsgFailed(("[%s] Write failed: cbToWrite=%RU32, cfWritten=%RU32, rc=%Rrc\n", 1023 pGstStream->szName, cbToWrite, cfGstWritten, rc)); 960 1024 break; 961 1025 } 962 1026 963 1027 if (pThis->Out.Cfg.Dbg.fEnabled) 964 DrvAudioHlpFileWrite(pHstStream->Out.Dbg.pFileStreamWrite, pvBuf, cb Buf, 0 /* fFlags */);1028 DrvAudioHlpFileWrite(pHstStream->Out.Dbg.pFileStreamWrite, pvBuf, cbToWrite, 0 /* fFlags */); 965 1029 966 1030 #ifdef VBOX_WITH_STATISTICS 967 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesWritten, cf Written);1031 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesWritten, cfGstWritten); 968 1032 #endif 969 uint32_t cfMixed = 0; 970 if (cfWritten) 971 { 972 int rc2 = AudioMixBufMixToParentEx(&pGstStream->MixBuf, 0 /* cSrcOffset */, cfWritten /* cSrcFrames */, 973 &cfMixed /* pcSrcMixed */); 974 if ( RT_FAILURE(rc2) 975 || cfMixed < cfWritten) 1033 uint32_t cfGstMixed = 0; 1034 if (cfGstWritten) 1035 { 1036 int rc2 = AudioMixBufMixToParentEx(&pGstStream->MixBuf, 0 /* cSrcOffset */, cfGstWritten /* cSrcFrames */, 1037 &cfGstMixed /* pcSrcMixed */); 1038 if (RT_FAILURE(rc2)) 976 1039 { 977 AssertMsgFailed(("[%s] Mixing failed: cbBuf=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n", 978 pGstStream->szName, cbBuf, cfWritten, cfMixed, rc2)); 979 980 LogRel2(("Audio: Lost audio frames (%RU32) due to full host stream '%s', expect stuttering audio output\n", 981 cfWritten - cfMixed, pHstStream->szName)); 1040 AssertMsgFailed(("[%s] Mixing failed: cbToWrite=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n", 1041 pGstStream->szName, cbToWrite, cfGstWritten, cfGstMixed, rc2)); 1042 } 1043 else 1044 { 1045 Log3Func(("[%s] Buffer: Last written %RU64ms, writing %RU32 frames (%RU64ms), now filled with %RU64ms -- %RU8%%\n", 1046 pHstStream->szName, tsDeltaWrittenMs, cfGstWritten, DrvAudioHlpFramesToMs(&pHstStream->Cfg.Props, cfGstWritten), 1047 DrvAudioHlpFramesToMs(&pHstStream->Cfg.Props, AudioMixBufUsed(&pHstStream->MixBuf)), 1048 AudioMixBufUsed(&pHstStream->MixBuf) * 100 / AudioMixBufSize(&pHstStream->MixBuf))); 1049 1050 pHstStream->tsLastReadWrittenMs = RTTimeMilliTS(); 982 1051 983 1052 /* Keep going. */ … … 987 1056 rc = rc2; 988 1057 989 cbWrittenTotal = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cf Written);1058 cbWrittenTotal = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cfGstWritten); 990 1059 991 1060 #ifdef VBOX_WITH_STATISTICS 992 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesMixedOut, cf Mixed);993 Assert(cf Written >= cfMixed);994 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesLostOut, cf Written - cfMixed);1061 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesMixedOut, cfGstMixed); 1062 Assert(cfGstWritten >= cfGstMixed); 1063 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesLostOut, cfGstWritten - cfGstMixed); 995 1064 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesWritten, cbWrittenTotal); 996 1065 STAM_COUNTER_ADD(&pGstStream->Out.StatBytesTotalWritten, cbWrittenTotal); … … 998 1067 } 999 1068 1000 Log3Func(("[%s] cb Buf=%RU32, cUsed=%RU32, cLive=%RU32, cWritten=%RU32, cMixed=%RU32, rc=%Rrc\n",1001 pGstStream->szName, 1002 cbBuf, AudioMixBufUsed(&pGstStream->MixBuf), AudioMixBufLive(&pGstStream->MixBuf), cfWritten, cfMixed, rc));1069 Log3Func(("[%s] cbToWrite=%RU32, cfUsed=%RU32, cfLive=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n", 1070 pGstStream->szName, cbToWrite, AudioMixBufUsed(&pGstStream->MixBuf), 1071 AudioMixBufLive(&pGstStream->MixBuf), cfGstWritten, cfGstMixed, rc)); 1003 1072 1004 1073 } while (0); … … 1189 1258 /* No audio frames to transfer from guest to host (anymore)? 1190 1259 * Then try closing this stream if marked so in the next block. */ 1191 fTryClosePending = AudioMixBufLive(&pHstStream->MixBuf) == 0; 1260 const uint32_t cfLive = AudioMixBufLive(&pHstStream->MixBuf); 1261 fTryClosePending = cfLive == 0; 1262 Log3Func(("[%s] fTryClosePending=%RTbool, cfLive=%RU32\n", pHstStream->szName, fTryClosePending, cfLive)); 1192 1263 } 1193 1264 else … … 1199 1270 && fTryClosePending) 1200 1271 { 1201 if (pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional to implement. */ 1272 /* Tell the backend to drain the stream, that is, play the remaining (buffered) data 1273 * on the backend side. */ 1274 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DRAIN); 1275 if (rc == VERR_NOT_SUPPORTED) /* Not all backends support draining. */ 1276 rc = VINF_SUCCESS; 1277 1278 if (RT_SUCCESS(rc)) 1202 1279 { 1203 const uint32_t cxPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pHstStream->pvBackend); 1204 Log3Func(("[%s] cxPending=%RU32\n", pHstStream->szName, cxPending)); 1205 1206 /* Only try close pending if no audio data is pending on the backend-side anymore. */ 1207 fTryClosePending = cxPending == 0; 1280 if (pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional to implement. */ 1281 { 1282 const uint32_t cxPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pHstStream->pvBackend); 1283 Log3Func(("[%s] cxPending=%RU32\n", pHstStream->szName, cxPending)); 1284 1285 /* Only try close pending if no audio data is pending on the backend-side anymore. */ 1286 fTryClosePending = cxPending == 0; 1287 } 1288 1289 if (fTryClosePending) 1290 { 1291 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName)); 1292 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE); 1293 if (RT_FAILURE(rc)) 1294 LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pHstStream->szName, rc)); 1295 } 1208 1296 } 1209 1210 if (fTryClosePending)1211 {1212 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));1213 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);1214 if (RT_FAILURE(rc))1215 LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pHstStream->szName, rc));1216 }1217 1297 } 1218 1298 … … 1220 1300 1221 1301 /* Update timestamps. */ 1222 pHstStream->tsLastIterateMS = RTTimeMilliTS(); 1223 pGstStream->tsLastIterateMS = RTTimeMilliTS(); 1302 pGstStream->tsLastIteratedMs = RTTimeMilliTS(); 1224 1303 1225 1304 if (RT_FAILURE(rc)) … … 1477 1556 uint32_t cfPlayedTotal = 0; 1478 1557 1558 if (!pThis->pHostDrvAudio) 1559 return VINF_SUCCESS; 1560 1561 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream); 1562 AssertPtr(pHstStream); 1563 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL; 1564 AssertPtr(pGstStream); 1565 1566 AssertReleaseMsgReturn(pHstStream != NULL, 1567 ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n", 1568 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx), 1569 VERR_NOT_AVAILABLE); 1570 AssertReleaseMsgReturn(pGstStream != NULL, 1571 ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n", 1572 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx), 1573 VERR_NOT_AVAILABLE); 1574 1575 PDMAUDIOSTREAMSTS stsHstStream = pHstStream->fStatus; 1576 #ifdef LOG_ENABLED 1577 char *pszStreamSts = dbgAudioStreamStatusToStr(stsHstStream); 1578 Log3Func(("[%s] Start: stsHstStream=%s\n", pHstStream->szName, pszStreamSts)); 1579 RTStrFree(pszStreamSts); 1580 #endif /* LOG_ENABLED */ 1581 1479 1582 do 1480 1583 { 1481 if (!pThis->pHostDrvAudio)1482 break;1483 1484 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);1485 AssertPtr(pHstStream);1486 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;1487 AssertPtr(pGstStream);1488 1489 AssertReleaseMsgBreakStmt(pHstStream != NULL,1490 ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",1491 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),1492 rc = VERR_NOT_AVAILABLE);1493 AssertReleaseMsgBreakStmt(pGstStream != NULL,1494 ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",1495 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),1496 rc = VERR_NOT_AVAILABLE);1497 1498 1584 /* 1499 1585 * Check if the backend is ready to operate. 1500 1586 */ 1501 1502 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus); 1503 PDMAUDIOSTREAMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend); 1587 if (!(stsHstStream & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Stream disabled? Bail out. */ 1588 break; 1589 1590 uint32_t cfLive = AudioMixBufLive(&pHstStream->MixBuf); 1591 const uint8_t uLivePercent = (100 * cfLive) / AudioMixBufSize(&pHstStream->MixBuf); 1592 1593 if (!pHstStream->tsLastPlayedCapturedMs) 1594 pHstStream->tsLastPlayedCapturedMs = RTTimeMilliTS(); 1595 uint64_t tsDeltaPlayedCapturedMs = RTTimeMilliTS() - pHstStream->tsLastPlayedCapturedMs; 1596 pHstStream->tsLastPlayedCapturedMs = RTTimeMilliTS(); 1597 1504 1598 #ifdef LOG_ENABLED 1505 char *pszBackendSts = dbgAudioStreamStatusToStr(stsBackend); 1506 Log3Func(("[%s] Start: stsBackend=%s\n", pHstStream->szName, pszBackendSts)); 1507 RTStrFree(pszBackendSts); 1508 #endif /* LOG_ENABLED */ 1509 if (!(stsBackend & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Backend disabled? Bail out. */ 1510 break; 1511 1512 uint32_t cfToPlay = AudioMixBufLive(&pHstStream->MixBuf); 1513 1514 if (pThis->pHostDrvAudio->pfnStreamPlayBegin) 1515 pThis->pHostDrvAudio->pfnStreamPlayBegin(pThis->pHostDrvAudio, pHstStream->pvBackend); 1516 1517 if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED)) 1518 { 1519 rc = drvAudioStreamPlayNonInterleaved(pThis, pHstStream, cfToPlay, &cfPlayedTotal); 1520 } 1521 else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW) 1522 { 1523 rc = drvAudioStreamPlayRaw(pThis, pHstStream, cfToPlay, &cfPlayedTotal); 1524 } 1525 else 1526 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED); 1527 1528 if (pThis->pHostDrvAudio->pfnStreamPlayEnd) 1529 pThis->pHostDrvAudio->pfnStreamPlayEnd(pThis->pHostDrvAudio, pHstStream->pvBackend); 1530 1531 #ifdef LOG_ENABLED 1532 uint32_t cfLive = 0; 1599 Log3Func(("[%s] Buffer: Last played %RU64ms, filled with %RU64ms (%RU8%%) total, " 1600 "(cfLive=%RU32, fThresholdReached=%RTbool)\n", 1601 pHstStream->szName, tsDeltaPlayedCapturedMs, DrvAudioHlpFramesToMs(&pHstStream->Cfg.Props, cfLive), 1602 uLivePercent, cfLive, pHstStream->fThresholdReached)); 1533 1603 #endif 1604 bool fDoPlay = pHstStream->fThresholdReached; 1605 bool fJustStarted = false; 1606 if (!fDoPlay) 1607 { 1608 /* Did we reach the backend's playback (pre-buffering) threshold? Can be 0 if no threshold set. */ 1609 if (cfLive >= pHstStream->Cfg.Backend.cfPreBuf) 1610 { 1611 LogRel2(("Audio: Stream '%s' buffering complete\n", pHstStream->szName)); 1612 fDoPlay = true; 1613 } 1614 1615 /* Some audio files are shorter than the pre-buffering level (e.g. the "click" Explorer sounds on some Windows guests), 1616 * so make sure that we also play those by checking if the stream already is pending disable mode, even if we didn't 1617 * hit the pre-buffering watermark yet. 1618 * 1619 * To reproduce, use "Windows Navigation Start.wav" on Windows 7 (2824 samples). */ 1620 if ( cfLive 1621 && pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE) 1622 { 1623 LogRel2(("Audio: Stream '%s' buffering complete (short sound)\n", pHstStream->szName)); 1624 fDoPlay = true; 1625 } 1626 1627 if (fDoPlay) 1628 { 1629 pHstStream->fThresholdReached = true; 1630 fJustStarted = true; 1631 LogRel2(("Audio: Stream '%s' started playing\n", pHstStream->szName)); 1632 } 1633 else /* Not yet, so still buffering audio data. */ 1634 LogRel2(("Audio: Stream '%s' is buffering (%RU8%% complete)\n", 1635 pHstStream->szName, (100 * cfLive) / pHstStream->Cfg.Backend.cfPreBuf)); 1636 } 1637 1638 if (fDoPlay) 1639 { 1640 if (pHstStream->tsLastPlayedCapturedMs < 2) 1641 break; 1642 1643 uint32_t cfToPlay; 1644 if (fJustStarted) 1645 cfToPlay = pHstStream->Cfg.Backend.cfPeriod; 1646 else 1647 cfToPlay = DrvAudioHlpMsToFrames(&pHstStream->Cfg.Props, tsDeltaPlayedCapturedMs); 1648 1649 Log3Func(("[%s] Buffer: fJustStarted=%RTbool, cfLive=%RU32, cfToPlay=%RU32\n", 1650 pHstStream->szName, fJustStarted, cfLive, cfToPlay)); 1651 1652 /* Did we reach a buffer underrun? Do pre-buffering again. 1653 * If we're in pending disabled mode, try to play (drain) the remaining audio data. */ 1654 if ( !(pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE) 1655 && !fJustStarted 1656 && cfLive < cfToPlay) 1657 { 1658 pHstStream->fThresholdReached = false; 1659 LogRel2(("Audio: Stream '%s' buffer underrun (total %RU8%%, which is %RU8%% of a period), buffering ...\n", 1660 pHstStream->szName, uLivePercent, (100 * cfLive) / pHstStream->Cfg.Backend.cfPeriod)); 1661 break; 1662 } 1663 1664 if (cfToPlay > cfLive) /* Don't try to play more than available. */ 1665 cfToPlay = cfLive; 1666 1667 Log3Func(("[%s] Buffer: Playing %RU32 frames (%RU64ms since last playback)\n", 1668 pHstStream->szName, cfToPlay, tsDeltaPlayedCapturedMs)); 1669 1670 if (pThis->pHostDrvAudio->pfnStreamPlayBegin) 1671 pThis->pHostDrvAudio->pfnStreamPlayBegin(pThis->pHostDrvAudio, pHstStream->pvBackend); 1672 1673 if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED)) 1674 { 1675 rc = drvAudioStreamPlayNonInterleaved(pThis, pHstStream, cfToPlay, &cfPlayedTotal); 1676 } 1677 else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW) 1678 { 1679 rc = drvAudioStreamPlayRaw(pThis, pHstStream, cfToPlay, &cfPlayedTotal); 1680 } 1681 else 1682 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED); 1683 1684 if (pThis->pHostDrvAudio->pfnStreamPlayEnd) 1685 pThis->pHostDrvAudio->pfnStreamPlayEnd(pThis->pHostDrvAudio, pHstStream->pvBackend); 1686 } 1687 1534 1688 if (RT_SUCCESS(rc)) 1535 1689 { … … 1541 1695 STAM_COUNTER_ADD (&pHstStream->Out.StatFramesPlayed, cfPlayedTotal); 1542 1696 #endif 1697 } 1698 1699 } while (0); 1543 1700 1544 1701 #ifdef LOG_ENABLED 1545 cfLive = AudioMixBufLive(&pHstStream->MixBuf); 1546 #endif 1547 } 1548 1549 #ifdef LOG_ENABLED 1550 pszBackendSts = dbgAudioStreamStatusToStr(stsBackend); 1551 Log3Func(("[%s] End: stsBackend=%s, cfLive=%RU32, cfPlayedTotal=%RU32, rc=%Rrc\n", 1552 pHstStream->szName, pszBackendSts, cfLive, cfPlayedTotal, rc)); 1553 RTStrFree(pszBackendSts); 1702 uint32_t cfLive = AudioMixBufLive(&pHstStream->MixBuf); 1703 pszStreamSts = dbgAudioStreamStatusToStr(stsHstStream); 1704 Log3Func(("[%s] End: stsHstStream=%s, cfLive=%RU32, cfPlayedTotal=%RU32, rc=%Rrc\n", 1705 pHstStream->szName, pszStreamSts, cfLive, cfPlayedTotal, rc)); 1706 RTStrFree(pszStreamSts); 1554 1707 #endif /* LOG_ENABLED */ 1555 1556 } while (0);1557 1708 1558 1709 int rc2 = RTCritSectLeave(&pThis->CritSect); … … 1791 1942 */ 1792 1943 1793 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus); 1794 PDMAUDIOSTREAMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend); 1944 PDMAUDIOSTREAMSTS stsHstStream = pHstStream->fStatus; 1795 1945 #ifdef LOG_ENABLED 1796 char *psz BackendSts = dbgAudioStreamStatusToStr(stsBackend);1797 Log3Func(("[%s] Start: sts Backend=%s\n", pHstStream->szName, pszBackendSts));1798 RTStrFree(psz BackendSts);1946 char *pszStreamSts = dbgAudioStreamStatusToStr(stsHstStream); 1947 Log3Func(("[%s] Start: stsHstStream=%s\n", pHstStream->szName, pszStreamSts)); 1948 RTStrFree(pszStreamSts); 1799 1949 #endif /* LOG_ENABLED */ 1800 if (!(sts Backend & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Backenddisabled? Bail out. */1950 if (!(stsHstStream & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Stream disabled? Bail out. */ 1801 1951 break; 1802 1952 … … 1823 1973 1824 1974 #ifdef LOG_ENABLED 1825 psz BackendSts = dbgAudioStreamStatusToStr(stsBackend);1826 Log3Func(("[%s] End: sts Backend=%s, cfCaptured=%RU32, rc=%Rrc\n",1827 pHstStream->szName, psz BackendSts, cfCaptured, rc));1828 RTStrFree(psz BackendSts);1975 pszStreamSts = dbgAudioStreamStatusToStr(stsHstStream); 1976 Log3Func(("[%s] End: stsHstStream=%s, cfCaptured=%RU32, rc=%Rrc\n", 1977 pHstStream->szName, pszStreamSts, cfCaptured, rc)); 1978 RTStrFree(pszStreamSts); 1829 1979 #endif /* LOG_ENABLED */ 1830 1980 … … 2191 2341 2192 2342 /** 2343 * Retrieves an audio configuration from the specified CFGM node. 2344 * 2345 * @return VBox status code. 2346 * @param pThis Driver instance to be called. 2347 * @param pCfg Where to store the retrieved audio configuration to. 2348 * @param pNode Where to get the audio configuration from. 2349 */ 2350 static int drvAudioGetCfgFromCFGM(PDRVAUDIO pThis, PDRVAUDIOCFG pCfg, PCFGMNODE pNode) 2351 { 2352 RT_NOREF(pThis); 2353 2354 /* Debug stuff. */ 2355 CFGMR3QueryBoolDef(pNode, "DebugEnabled", &pCfg->Dbg.fEnabled, false); 2356 int rc2 = CFGMR3QueryString(pNode, "DebugPathOut", pCfg->Dbg.szPathOut, sizeof(pCfg->Dbg.szPathOut)); 2357 if ( RT_FAILURE(rc2) 2358 || !strlen(pCfg->Dbg.szPathOut)) 2359 { 2360 RTStrPrintf(pCfg->Dbg.szPathOut, sizeof(pCfg->Dbg.szPathOut), VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH); 2361 } 2362 2363 if (pCfg->Dbg.fEnabled) 2364 LogRel(("Audio: Debugging enabled (audio data written to '%s')\n", pCfg->Dbg.szPathOut)); 2365 2366 /* Buffering stuff. */ 2367 CFGMR3QueryU32Def(pNode, "PeriodMs", &pCfg->uPeriodMs, 0); 2368 CFGMR3QueryU32Def(pNode, "BufferSizeMs", &pCfg->uBufferSizeMs, 0); 2369 CFGMR3QueryU32Def(pNode, "PreBufferMs", &pCfg->uPreBufMs, UINT32_MAX /* No custom value set */); 2370 2371 LogFunc(("pCfg=%p, uPeriodMs=%RU32, uBufferSizeMs=%RU32, uPreBufMs=%RU32\n", 2372 pCfg, pCfg->uPeriodMs, pCfg->uBufferSizeMs, pCfg->uPreBufMs)); 2373 2374 return VINF_SUCCESS; 2375 } 2376 2377 /** 2193 2378 * Intializes an audio driver instance. 2194 2379 * … … 2227 2412 CFGMR3QueryBoolDef(pThis->pCFGMNode, "OutputEnabled", &pThis->Out.fEnabled, false); 2228 2413 2229 /* Input configuration. */2230 /** @todo Separate debugging stuff if needed. Later. */2231 CFGMR3QueryBoolDef(pThis->pCFGMNode, "DebugEnabled", &pThis->In.Cfg.Dbg.fEnabled, false);2232 rc2 = CFGMR3QueryString(pThis->pCFGMNode, "DebugPathOut", pThis->In.Cfg.Dbg.szPathOut, sizeof(pThis->In.Cfg.Dbg.szPathOut));2233 if ( RT_FAILURE(rc2)2234 || !strlen(pThis->In.Cfg.Dbg.szPathOut))2235 {2236 RTStrPrintf(pThis->In.Cfg.Dbg.szPathOut, sizeof(pThis->In.Cfg.Dbg.szPathOut), VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH);2237 }2238 2239 if (pThis->In.Cfg.Dbg.fEnabled)2240 LogRel(("Audio: Debugging input enabled (audio data written to '%s')\n", pThis->In.Cfg.Dbg.szPathOut));2241 2242 /* Output configuration. */2243 CFGMR3QueryBoolDef(pThis->pCFGMNode, "DebugEnabled", &pThis->Out.Cfg.Dbg.fEnabled, false);2244 rc2 = CFGMR3QueryString(pThis->pCFGMNode, "DebugPathOut", pThis->Out.Cfg.Dbg.szPathOut, sizeof(pThis->Out.Cfg.Dbg.szPathOut));2245 if ( RT_FAILURE(rc2)2246 || !strlen(pThis->Out.Cfg.Dbg.szPathOut))2247 {2248 RTStrPrintf(pThis->Out.Cfg.Dbg.szPathOut, sizeof(pThis->Out.Cfg.Dbg.szPathOut), VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH);2249 }2250 2251 if (pThis->Out.Cfg.Dbg.fEnabled)2252 LogRel(("Audio: Debugging output enabled (audio data written to '%s')\n", pThis->Out.Cfg.Dbg.szPathOut));2253 2254 2414 LogRel2(("Audio: Initial status for driver '%s': Input is %s, output is %s\n", 2255 2415 pThis->szName, pThis->In.fEnabled ? "enabled" : "disabled", pThis->Out.fEnabled ? "enabled" : "disabled")); 2416 2417 /* 2418 * Load configurations. 2419 */ 2420 rc = drvAudioGetCfgFromCFGM(pThis, &pThis->In.Cfg, pThis->pCFGMNode); 2421 if (RT_SUCCESS(rc)) 2422 rc = drvAudioGetCfgFromCFGM(pThis, &pThis->Out.Cfg, pThis->pCFGMNode); 2256 2423 2257 2424 LogFunc(("[%s] rc=%Rrc\n", pThis->szName, rc)); … … 3063 3230 * @param pHstStream (Host) audio stream to use for creating the stream on the backend side. 3064 3231 * @param pCfgReq Requested audio stream configuration to use for stream creation. 3065 * @param pCfgAcq Acquired audio stream configuration returned by the backend. Optional, can be NULL.3232 * @param pCfgAcq Acquired audio stream configuration returned by the backend. 3066 3233 */ 3067 3234 static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis, … … 3071 3238 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER); 3072 3239 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); 3073 /* pCfgAcq is optional. */3240 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 3074 3241 3075 3242 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST, … … 3079 3246 ("Stream '%s' already initialized in backend\n", pHstStream->szName)); 3080 3247 3248 /* Get the right configuration for the stream to be created. */ 3249 PDRVAUDIOCFG pDrvCfg = pCfgReq->enmDir == PDMAUDIODIR_IN ? &pThis->In.Cfg : &pThis->Out.Cfg; 3250 3251 /* Fill in the tweakable parameters into the requested host configuration. 3252 * All parameters in principle can be changed and returned by the backend via the acquired configuration. */ 3253 3254 /* 3255 * Period size 3256 */ 3257 if (pDrvCfg->uPeriodMs) 3258 pCfgReq->Backend.cfPeriod = DrvAudioHlpMsToFrames(&pCfgReq->Props, pDrvCfg->uPeriodMs); 3259 else /* Set default period size. */ 3260 pCfgReq->Backend.cfPeriod = DrvAudioHlpMsToFrames(&pCfgReq->Props, 50 /* ms */); 3261 3262 LogRel2(("Audio: Using %s period size (%RU32ms, %RU32 frames) for stream '%s'\n", 3263 pDrvCfg->uPeriodMs ? "custom" : "default", DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfPeriod), 3264 pCfgReq->Backend.cfPeriod, pHstStream->szName)); 3265 3266 /* 3267 * Buffer size 3268 */ 3269 if (pDrvCfg->uBufferSizeMs) 3270 pCfgReq->Backend.cfBufferSize = DrvAudioHlpMsToFrames(&pCfgReq->Props, pDrvCfg->uBufferSizeMs); 3271 else /* Set default buffer size. */ 3272 pCfgReq->Backend.cfBufferSize = DrvAudioHlpMsToFrames(&pCfgReq->Props, 200 /* ms */); 3273 3274 LogRel2(("Audio: Using %s buffer size (%RU32ms, %RU32 frames) for stream '%s'\n", 3275 pDrvCfg->uBufferSizeMs ? "custom" : "default", DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize), 3276 pCfgReq->Backend.cfBufferSize, pHstStream->szName)); 3277 3278 /* 3279 * Pre-buffering size 3280 */ 3281 if (pDrvCfg->uPreBufMs != UINT32_MAX) 3282 { 3283 if (!pDrvCfg->uPreBufMs) /* Pre-buffering is set to disabled. */ 3284 LogRel2(("Audio: Using custom pre-buffering (disabled) for stream '%s'\n", pHstStream->szName)); 3285 pCfgReq->Backend.cfPreBuf = DrvAudioHlpMsToFrames(&pCfgReq->Props, pDrvCfg->uPreBufMs); 3286 } 3287 else /* Set default pre-buffering size. */ 3288 pCfgReq->Backend.cfPreBuf = pCfgReq->Backend.cfBufferSize; 3289 3290 LogRel2(("Audio: Using %s pre-buffering size (%RU32ms, %RU32 frames) for stream '%s'\n", 3291 pDrvCfg->uPreBufMs != UINT32_MAX ? "custom" : "default", DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfPreBuf), 3292 pCfgReq->Backend.cfPreBuf, pHstStream->szName)); 3293 3294 /* 3295 * Validate input. 3296 */ 3297 if (pCfgReq->Backend.cfBufferSize < pCfgReq->Backend.cfPeriod) 3298 { 3299 LogRel(("Audio: Error for stream '%s': Buffer size (%RU32ms) must not be smaller than the period size (%RU32ms)\n", 3300 pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize), 3301 DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfPeriod))); 3302 return VERR_INVALID_PARAMETER; 3303 } 3304 3305 if ( pCfgReq->Backend.cfPreBuf != UINT32_MAX /* Custom pre-buffering set? */ 3306 && pCfgReq->Backend.cfPreBuf) 3307 { 3308 if (pCfgReq->Backend.cfBufferSize < pCfgReq->Backend.cfPreBuf) 3309 { 3310 LogRel(("Audio: Error for stream '%s': Pre-buffering size (%RU32ms) must not be bigger than the buffer size (%RU32ms)\n", 3311 pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfPreBuf), 3312 DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize))); 3313 return VERR_INVALID_PARAMETER; 3314 } 3315 } 3316 3081 3317 /* Make the acquired host configuration the requested host configuration initially, 3082 3318 * in case the backend does not report back an acquired configuration. */ 3083 PDMAUDIOSTREAMCFG CfgAcq; 3084 int rc = DrvAudioHlpStreamCfgCopy(&CfgAcq, pCfgReq); 3319 int rc = DrvAudioHlpStreamCfgCopy(pCfgAcq, pCfgReq); 3085 3320 if (RT_FAILURE(rc)) 3086 3321 { … … 3090 3325 } 3091 3326 3092 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream->pvBackend, pCfgReq, &CfgAcq);3327 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream->pvBackend, pCfgReq, pCfgAcq); 3093 3328 if (RT_FAILURE(rc)) 3094 3329 { … … 3102 3337 3103 3338 /* Validate acquired configuration. */ 3104 if (!DrvAudioHlpStreamCfgIsValid( &CfgAcq))3339 if (!DrvAudioHlpStreamCfgIsValid(pCfgAcq)) 3105 3340 { 3106 3341 LogRel(("Audio: Creating stream '%s' returned an invalid backend configuration, skipping\n", pHstStream->szName)); 3107 3342 return VERR_INVALID_PARAMETER; 3343 } 3344 3345 /* Let the user know that the backend changed one of the user's custom values requested above. */ 3346 if ( pDrvCfg->uBufferSizeMs 3347 && pCfgAcq->Backend.cfBufferSize != pCfgReq->Backend.cfBufferSize) 3348 { 3349 LogRel2(("Audio: Custom buffer size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n", 3350 pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgAcq->Props, pCfgAcq->Backend.cfBufferSize), pCfgAcq->Backend.cfBufferSize)); 3351 } 3352 3353 if ( pDrvCfg->uPeriodMs 3354 && pCfgAcq->Backend.cfPeriod != pCfgReq->Backend.cfPeriod) 3355 { 3356 LogRel2(("Audio: Custom period size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n", 3357 pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgAcq->Props, pCfgAcq->Backend.cfPeriod), pCfgAcq->Backend.cfPeriod)); 3358 } 3359 3360 if ( pDrvCfg->uPreBufMs != UINT32_MAX 3361 && pCfgAcq->Backend.cfPreBuf != pCfgReq->Backend.cfPreBuf) 3362 { 3363 LogRel2(("Audio: Custom pre-buffering size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n", 3364 pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgAcq->Props, pCfgAcq->Backend.cfPreBuf), pCfgAcq->Backend.cfPreBuf)); 3108 3365 } 3109 3366 … … 3112 3369 * at some later point in time. */ 3113 3370 pHstStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_INITIALIZED; 3114 3115 if (pCfgAcq)3116 {3117 int rc2 = DrvAudioHlpStreamCfgCopy(pCfgAcq, &CfgAcq);3118 AssertRC(rc2);3119 }3120 3371 3121 3372 return VINF_SUCCESS; -
trunk/src/VBox/Devices/Audio/DrvAudio.h
r73230 r73370 79 79 typedef struct DRVAUDIOCFG 80 80 { 81 /** Configures the period size (in audio frames).82 * This value reflects the number of audio framesin between each hardware interrupt on the81 /** Configures the period size (in ms). 82 * This value reflects the time in between each hardware interrupt on the 83 83 * backend (host) side. */ 84 uint32_t cfPeriod; 85 /** Configures the (ring) buffer size (in audio frames). Often is a multiple of cfPeriod. */ 86 uint32_t cfBufferSize; 87 /** Configures the pre-buffering size (in audio frames). 88 * Frames needed in buffer before the stream becomes active (pre buffering). 89 * The bigger this value is, the more latency for the stream will occur. */ 90 uint32_t cfPreBuf; 84 uint32_t uPeriodMs; 85 /** Configures the (ring) buffer size (in ms). Often is a multiple of uPeriodMs. */ 86 uint32_t uBufferSizeMs; 87 /** Configures the pre-buffering size (in ms). 88 * Time needed in buffer before the stream becomes active (pre buffering). 89 * The bigger this value is, the more latency for the stream will occur. 90 * Set to 0 to disable pre-buffering completely. 91 * By default set to UINT32_MAX if not set to a custom value. */ 92 uint32_t uPreBufMs; 91 93 /** The driver's debugging configuration. */ 92 94 struct -
trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp
r73097 r73370 123 123 static ALSAAUDIOCFG s_ALSAConf = 124 124 { 125 #ifdef HIGH_LATENCY126 1,127 1,128 #else129 125 0, 130 126 0, 131 #endif132 127 "default", 133 128 "default", 134 #ifdef HIGH_LATENCY 135 400000, 136 400000 / 4, 137 400000, 138 400000 / 4, 139 #else 140 # define DEFAULT_BUFFER_SIZE 1024 141 # define DEFAULT_PERIOD_SIZE 256 142 DEFAULT_BUFFER_SIZE * 4, 143 DEFAULT_PERIOD_SIZE * 4, 144 DEFAULT_BUFFER_SIZE, 145 DEFAULT_PERIOD_SIZE, 146 #endif 147 0, 129 # define DEFAULT_PERIOD_FRAMES 4410 /* 100ms for 44,1 kHz. */ 130 # define DEFAULT_BUFFER_FRAMES DEFAULT_PERIOD_FRAMES * 2 /* Double (buffering) the period size. */ 131 DEFAULT_BUFFER_FRAMES, 132 DEFAULT_PERIOD_FRAMES, 133 DEFAULT_BUFFER_FRAMES, 134 DEFAULT_PERIOD_FRAMES, 135 DEFAULT_PERIOD_FRAMES * 2, /* Threshold, using double the period size to avoid stutters. */ 148 136 0, 149 137 0, … … 180 168 int resample; 181 169 int nchannels; 170 /** Buffer size (in audio frames). */ 182 171 unsigned long buffer_size; 172 /** Periods (in audio frames). */ 183 173 unsigned long period_size; 184 snd_pcm_uframes_t samples; 174 /** For playback: Starting to play threshold (in audio frames). 175 * For Capturing: Starting to capture threshold (in audio frames). */ 176 unsigned long threshold; 185 177 } ALSAAUDIOSTREAMCFG, *PALSAAUDIOSTREAMCFG; 186 178 … … 288 280 289 281 290 static int alsaGetSampleShift(snd_pcm_format_t fmt, unsigned *puShift)291 {292 AssertPtrReturn(puShift, VERR_INVALID_POINTER);293 294 switch (fmt)295 {296 case SND_PCM_FORMAT_S8:297 case SND_PCM_FORMAT_U8:298 *puShift = 0;299 break;300 301 case SND_PCM_FORMAT_S16_LE:302 case SND_PCM_FORMAT_U16_LE:303 case SND_PCM_FORMAT_S16_BE:304 case SND_PCM_FORMAT_U16_BE:305 *puShift = 1;306 break;307 308 case SND_PCM_FORMAT_S32_LE:309 case SND_PCM_FORMAT_U32_LE:310 case SND_PCM_FORMAT_S32_BE:311 case SND_PCM_FORMAT_U32_BE:312 *puShift = 2;313 break;314 315 default:316 AssertMsgFailed(("Format %ld not supported\n", fmt));317 return VERR_NOT_SUPPORTED;318 }319 320 return VINF_SUCCESS;321 }322 323 324 282 static int alsaStreamSetThreshold(snd_pcm_t *phPCM, snd_pcm_uframes_t threshold) 325 283 { … … 395 353 } 396 354 355 LogFlowFuncLeaveRC(rc); 397 356 return rc; 398 357 } … … 612 571 } 613 572 573 LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev)); 574 614 575 int err = snd_pcm_open(&phPCM, pszDev, 615 576 fIn ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, … … 622 583 } 623 584 624 LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev)); 585 err = snd_pcm_nonblock(phPCM, 1); 586 if (err < 0) 587 { 588 LogRel(("ALSA: Error setting output non-blocking mode: %s\n", snd_strerror(err))); 589 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 590 break; 591 } 625 592 626 593 snd_pcm_hw_params_t *pHWParms; … … 683 650 if (!buffer_size) 684 651 { 685 buffer_size = DEFAULT_BUFFER_ SIZE;686 period_size = DEFAULT_PERIOD_ SIZE;652 buffer_size = DEFAULT_BUFFER_FRAMES; 653 period_size = DEFAULT_PERIOD_FRAMES; 687 654 } 688 655 } … … 792 759 err = snd_pcm_hw_params_set_buffer_size_near(phPCM, 793 760 pHWParms, &buffer_size_f); 794 LogFunc(("Buffer size is: %RU32\n", buffer_size_f));795 761 if (err < 0) 796 762 { 797 LogRel(("ALSA: Failed to set buffer size %d: %s\n", 798 buffer_size_f, snd_strerror(err))); 763 LogRel(("ALSA: Failed to set near buffer size %RU32: %s\n", buffer_size_f, snd_strerror(err))); 799 764 rc = VERR_AUDIO_BACKEND_INIT_FAILED; 800 765 break; … … 821 786 } 822 787 823 LogFunc(("Buffer sample size is: %RU32\n", obt_buffer_size));824 825 788 snd_pcm_uframes_t obt_period_size; 826 789 int dir = 0; … … 833 796 } 834 797 835 Log Func(("Freq=%dHz, period size=%RU32, buffer size=%RU32\n",798 LogRel2(("ALSA: Frequency is %dHz, period size is %RU32 frames, buffer size is %RU32 fames\n", 836 799 pCfgReq->freq, obt_period_size, obt_buffer_size)); 837 800 … … 845 808 846 809 if ( !fIn 847 && s_ALSAConf.threshold) 848 { 849 unsigned uShift; 850 rc = alsaGetSampleShift(pCfgReq->fmt, &uShift); 851 if (RT_SUCCESS(rc)) 852 { 853 int bytes_per_sec = uFreq 854 << (cChannels == 2) 855 << uShift; 856 857 snd_pcm_uframes_t threshold 858 = (s_ALSAConf.threshold * bytes_per_sec) / 1000; 859 860 rc = alsaStreamSetThreshold(phPCM, threshold); 861 } 862 } 863 else 864 rc = VINF_SUCCESS; 810 && pCfgReq->threshold) 811 { 812 rc = alsaStreamSetThreshold(phPCM, pCfgReq->threshold); 813 if (RT_FAILURE(rc)) 814 break; 815 816 LogRel2(("ALSA: Threshold for playback set to %RU32 frames\n", pCfgReq->threshold)); 817 } 818 819 pCfgObt->fmt = pCfgReq->fmt; 820 pCfgObt->nchannels = cChannels; 821 pCfgObt->freq = uFreq; 822 pCfgObt->period_size = obt_period_size; 823 pCfgObt->buffer_size = obt_buffer_size; 824 pCfgObt->threshold = pCfgReq->threshold; 825 826 rc = VINF_SUCCESS; 865 827 } 866 828 while (0); … … 868 830 if (RT_SUCCESS(rc)) 869 831 { 870 pCfgObt->fmt = pCfgReq->fmt;871 pCfgObt->nchannels = cChannels;872 pCfgObt->freq = uFreq;873 pCfgObt->samples = obt_buffer_size;874 875 832 *pphPCM = phPCM; 876 833 } … … 953 910 954 911 return VINF_SUCCESS; 955 }956 957 958 static int drvHostALSAAudioStreamCtl(PALSAAUDIOSTREAM pStreamALSA, bool fPause)959 {960 int rc = VINF_SUCCESS;961 962 const bool fInput = pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN;963 964 int err;965 if (fPause)966 {967 err = snd_pcm_drop(pStreamALSA->phPCM);968 if (err < 0)969 {970 LogRel(("ALSA: Error stopping %s stream: %s\n", fInput ? "input" : "output", snd_strerror(err)));971 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */972 }973 }974 else975 {976 err = snd_pcm_prepare(pStreamALSA->phPCM);977 if (err < 0)978 {979 LogRel(("ALSA: Error preparing %s stream: %s\n", fInput ? "input" : "output", snd_strerror(err)));980 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */981 }982 else983 {984 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);985 986 if (fInput) /* Only start the PCM stream for input streams. */987 {988 err = snd_pcm_start(pStreamALSA->phPCM);989 if (err < 0)990 {991 LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err)));992 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */993 }994 }995 }996 }997 998 return rc;999 912 } 1000 913 … … 1324 1237 req.freq = pCfgReq->Props.uHz; 1325 1238 req.nchannels = pCfgReq->Props.cChannels; 1326 req.period_size = s_ALSAConf.period_size_out; /** @todo Make this configurable. */ 1327 req.buffer_size = s_ALSAConf.buffer_size_out; /** @todo Make this configurable. */ 1239 req.period_size = pCfgReq->Backend.cfPeriod; 1240 req.buffer_size = pCfgReq->Backend.cfBufferSize; 1241 req.threshold = pCfgReq->Backend.cfPreBuf; 1328 1242 1329 1243 ALSAAUDIOSTREAMCFG obt; … … 1339 1253 break; 1340 1254 1341 pCfgAcq->cFrameBufferHint = obt.samples * 4; 1342 1343 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER); 1344 1345 size_t cbBuf = obt.samples * PDMAUDIOSTREAMCFG_F2B(pCfgAcq, 1); 1346 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER); 1347 1348 pStreamALSA->pvBuf = RTMemAllocZ(cbBuf); 1255 pCfgAcq->Backend.cfPeriod = obt.period_size; 1256 pCfgAcq->Backend.cfBufferSize = obt.buffer_size; 1257 pCfgAcq->Backend.cfPreBuf = obt.threshold; 1258 1259 pStreamALSA->cbBuf = pCfgAcq->Backend.cfBufferSize * DrvAudioHlpPCMPropsBytesPerFrame(&pCfgAcq->Props); 1260 pStreamALSA->pvBuf = RTMemAllocZ(pStreamALSA->cbBuf); 1349 1261 if (!pStreamALSA->pvBuf) 1350 1262 { 1351 LogRel(("ALSA: Not enough memory for output DAC buffer (% RU32 samples, %zu bytes)\n", obt.samples, cbBuf));1263 LogRel(("ALSA: Not enough memory for output DAC buffer (%zu frames)\n", pCfgAcq->Backend.cfBufferSize)); 1352 1264 rc = VERR_NO_MEMORY; 1353 1265 break; 1354 1266 } 1355 1267 1356 pStreamALSA->cbBuf = cbBuf;1357 1268 pStreamALSA->phPCM = phPCM; 1358 1269 } … … 1379 1290 req.freq = pCfgReq->Props.uHz; 1380 1291 req.nchannels = pCfgReq->Props.cChannels; 1381 req.period_size = s_ALSAConf.period_size_in; /** @todo Make this configurable. */ 1382 req.buffer_size = s_ALSAConf.buffer_size_in; /** @todo Make this configurable. */ 1292 req.period_size = DrvAudioHlpMsToFrames(&pCfgReq->Props, 50 /* ms */); /** @todo Make this configurable. */ 1293 req.buffer_size = req.period_size * 2; /** @todo Make this configurable. */ 1294 req.threshold = req.period_size; 1383 1295 1384 1296 ALSAAUDIOSTREAMCFG obt; … … 1394 1306 break; 1395 1307 1396 pCfgAcq->cFrameBufferHint = obt.samples; 1397 1398 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER); 1399 1400 size_t cbBuf = obt.samples * PDMAUDIOSTREAMCFG_F2B(pCfgAcq, 1); 1401 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER); 1402 1403 pStreamALSA->pvBuf = RTMemAlloc(cbBuf); 1308 pCfgAcq->Backend.cfPeriod = obt.period_size; 1309 pCfgAcq->Backend.cfBufferSize = obt.buffer_size; 1310 /* No pre-buffering. */ 1311 1312 pStreamALSA->cbBuf = pCfgAcq->Backend.cfBufferSize * DrvAudioHlpPCMPropsBytesPerFrame(&pCfgAcq->Props); 1313 pStreamALSA->pvBuf = RTMemAlloc(pStreamALSA->cbBuf); 1404 1314 if (!pStreamALSA->pvBuf) 1405 1315 { 1406 LogRel(("ALSA: Not enough memory for input ADC buffer (% RU32 samples, %zu bytes)\n", obt.samples, cbBuf));1316 LogRel(("ALSA: Not enough memory for input ADC buffer (%zu frames)\n", pCfgAcq->Backend.cfBufferSize)); 1407 1317 rc = VERR_NO_MEMORY; 1408 1318 break; 1409 1319 } 1410 1320 1411 pStreamALSA->cbBuf = cbBuf;1412 1321 pStreamALSA->phPCM = phPCM; 1413 1322 } … … 1424 1333 static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd) 1425 1334 { 1426 int rc; 1335 int rc = VINF_SUCCESS; 1336 1337 int err; 1338 1427 1339 switch (enmStreamCmd) 1428 1340 { 1429 1341 case PDMAUDIOSTREAMCMD_ENABLE: 1430 1342 case PDMAUDIOSTREAMCMD_RESUME: 1431 rc = drvHostALSAAudioStreamCtl(pStreamALSA, false /* fStop */); 1432 break; 1343 { 1344 err = snd_pcm_prepare(pStreamALSA->phPCM); 1345 if (err < 0) 1346 { 1347 LogRel(("ALSA: Error preparing input stream: %s\n", snd_strerror(err))); 1348 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1349 } 1350 else 1351 { 1352 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED); 1353 1354 /* Only start the PCM stream for input streams. */ 1355 err = snd_pcm_start(pStreamALSA->phPCM); 1356 if (err < 0) 1357 { 1358 LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err))); 1359 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1360 } 1361 } 1362 1363 break; 1364 } 1433 1365 1434 1366 case PDMAUDIOSTREAMCMD_DISABLE: 1367 { 1368 err = snd_pcm_drop(pStreamALSA->phPCM); 1369 if (err < 0) 1370 { 1371 LogRel(("ALSA: Error disabling input %s stream: %s\n", snd_strerror(err))); 1372 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1373 } 1374 break; 1375 } 1376 1435 1377 case PDMAUDIOSTREAMCMD_PAUSE: 1436 rc = drvHostALSAAudioStreamCtl(pStreamALSA, true /* fStop */); 1437 break; 1378 { 1379 err = snd_pcm_drop(pStreamALSA->phPCM); 1380 if (err < 0) 1381 { 1382 LogRel(("ALSA: Error pausing input stream: %s\n", snd_strerror(err))); 1383 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1384 } 1385 break; 1386 } 1438 1387 1439 1388 default: 1440 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));1441 rc = VERR_INVALID_PARAMETER;1442 break;1443 } 1444 1389 rc = VERR_NOT_SUPPORTED; 1390 break; 1391 } 1392 1393 LogFlowFuncLeaveRC(rc); 1445 1394 return rc; 1446 1395 } … … 1449 1398 static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd) 1450 1399 { 1451 int rc; 1400 int rc = VINF_SUCCESS; 1401 1402 int err; 1403 1452 1404 switch (enmStreamCmd) 1453 1405 { 1454 1406 case PDMAUDIOSTREAMCMD_ENABLE: 1455 1407 case PDMAUDIOSTREAMCMD_RESUME: 1456 rc = drvHostALSAAudioStreamCtl(pStreamALSA, false /* fStop */); 1457 break; 1408 { 1409 err = snd_pcm_prepare(pStreamALSA->phPCM); 1410 if (err < 0) 1411 { 1412 LogRel(("ALSA: Error preparing output stream: %s\n", snd_strerror(err))); 1413 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1414 } 1415 else 1416 { 1417 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED); 1418 } 1419 1420 break; 1421 } 1458 1422 1459 1423 case PDMAUDIOSTREAMCMD_DISABLE: 1424 { 1425 err = snd_pcm_drop(pStreamALSA->phPCM); 1426 if (err < 0) 1427 { 1428 LogRel(("ALSA: Error disabling output stream: %s\n", snd_strerror(err))); 1429 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1430 } 1431 break; 1432 } 1433 1460 1434 case PDMAUDIOSTREAMCMD_PAUSE: 1461 rc = drvHostALSAAudioStreamCtl(pStreamALSA, true /* fStop */); 1462 break; 1435 { 1436 err = snd_pcm_drop(pStreamALSA->phPCM); 1437 if (err < 0) 1438 { 1439 LogRel(("ALSA: Error pausing output stream: %s\n", snd_strerror(err))); 1440 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1441 } 1442 break; 1443 } 1444 1445 case PDMAUDIOSTREAMCMD_DRAIN: 1446 { 1447 snd_pcm_state_t streamState = snd_pcm_state(pStreamALSA->phPCM); 1448 Log2Func(("Stream state is: %d\n", streamState)); 1449 1450 if ( streamState == SND_PCM_STATE_PREPARED 1451 || streamState == SND_PCM_STATE_RUNNING) 1452 { 1453 err = snd_pcm_nonblock(pStreamALSA->phPCM, 0); 1454 if (err < 0) 1455 { 1456 LogRel(("ALSA: Error disabling output non-blocking mode: %s\n", snd_strerror(err))); 1457 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1458 break; 1459 } 1460 1461 err = snd_pcm_drain(pStreamALSA->phPCM); 1462 if (err < 0) 1463 { 1464 LogRel(("ALSA: Error draining output: %s\n", snd_strerror(err))); 1465 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1466 break; 1467 } 1468 1469 err = snd_pcm_nonblock(pStreamALSA->phPCM, 1); 1470 if (err < 0) 1471 { 1472 LogRel(("ALSA: Error re-enabling output non-blocking mode: %s\n", snd_strerror(err))); 1473 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1474 } 1475 } 1476 break; 1477 } 1463 1478 1464 1479 default: 1465 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));1466 rc = VERR_INVALID_PARAMETER;1467 break;1468 } 1469 1480 rc = VERR_NOT_SUPPORTED; 1481 break; 1482 } 1483 1484 LogFlowFuncLeaveRC(rc); 1470 1485 return rc; 1471 1486 } … … 1693 1708 1694 1709 /** 1710 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending} 1711 */ 1712 static DECLCALLBACK(uint32_t) drvHostALSAStreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1713 { 1714 RT_NOREF(pInterface); 1715 AssertPtrReturn(pStream, 0); 1716 1717 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 1718 1719 snd_pcm_sframes_t cfDelay = 0; 1720 snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->phPCM); 1721 1722 int rc = VINF_SUCCESS; 1723 1724 AssertPtr(pStreamALSA->pCfg); 1725 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_OUT) 1726 { 1727 int rc2 = snd_pcm_delay(pStreamALSA->phPCM, &cfDelay); 1728 if (RT_SUCCESS(rc)) 1729 rc = rc2; 1730 1731 /* Make sure to check the stream's status. 1732 * If it's anything but SND_PCM_STATE_RUNNING, the delay is meaningless and therefore 0. */ 1733 if (enmState != SND_PCM_STATE_RUNNING) 1734 cfDelay = 0; 1735 } 1736 1737 /* Note: For input streams we never have pending data left. */ 1738 1739 Log2Func(("cfDelay=%RI32, enmState=%d, rc=%d\n", cfDelay, enmState, rc)); 1740 1741 return DrvAudioHlpFramesToBytes(&pStreamALSA->pCfg->Props, cfDelay); 1742 } 1743 1744 1745 /** 1695 1746 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus} 1696 1747 */ … … 1752 1803 /* IHostAudio */ 1753 1804 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostALSAAudio); 1805 pThis->IHostAudio.pfnStreamGetPending = drvHostALSAStreamGetPending; 1754 1806 1755 1807 return VINF_SUCCESS; -
trunk/src/VBox/Devices/Audio/DrvHostCoreAudio.cpp
r70318 r73370 5 5 6 6 /* 7 * Copyright (C) 2010-201 7Oracle Corporation7 * Copyright (C) 2010-2018 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 40 40 41 41 42 /* Audio Queue buffer configuration. */43 #define AQ_BUF_COUNT 32 /* Number of buffers. */44 #define AQ_BUF_SIZE 512 /* Size of each buffer in bytes. */45 #define AQ_BUF_TOTAL (AQ_BUF_COUNT * AQ_BUF_SIZE)46 #define AQ_BUF_SAMPLES (AQ_BUF_TOTAL / 4) /* Hardcoded 4 bytes per sample! */47 42 48 43 /* Enables utilizing the Core Audio converter unit for converting … … 379 374 AudioQueueRef audioQueue; 380 375 /** The audio buffers which are used with the above audio queue. */ 381 AudioQueueBufferRef audioBuffer[ AQ_BUF_COUNT];376 AudioQueueBufferRef audioBuffer[2]; 382 377 /** The acquired (final) audio format for this stream. */ 383 378 AudioStreamBasicDescription asbdStream; … … 1303 1298 return VERR_GENERAL_FAILURE; /** @todo Fudge! */ 1304 1299 1305 const size_t cbBufSize = AQ_BUF_SIZE; /** @todo Make this configurable! */1300 const size_t cbBufSize = DrvAudioHlpFramesToBytes(&pCAStream->pCfg->Props, pCAStream->pCfg->Backend.cfPeriod); 1306 1301 1307 1302 /* … … 2326 2321 2327 2322 rc = coreAudioStreamInitQueue(pCAStream, pCfgReq, pCfgAcq); 2328 if (RT_SUCCESS(rc))2329 {2330 pCfgAcq->cFrameBufferHint = AQ_BUF_SAMPLES; /** @todo Make this configurable. */2331 }2332 2323 if (RT_SUCCESS(rc)) 2333 2324 { -
trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp
r73097 r73370 104 104 typedef struct DSOUNDHOSTCFG 105 105 { 106 unsigned int msLatencyIn;107 unsigned int msLatencyOut;108 106 RTUUID uuidPlay; 109 107 LPCGUID pGuidPlay; … … 115 113 { 116 114 /** The stream's acquired configuration. */ 117 P PDMAUDIOSTREAMCFG pCfg;115 PDMAUDIOSTREAMCFG Cfg; 118 116 /** Buffer alignment. */ 119 117 uint8_t uAlign; … … 150 148 /** Offset of last play cursor within the DSB when last played. */ 151 149 DWORD offPlayCursorLastPlayed; 152 /** Total amount (in bytes) written . */150 /** Total amount (in bytes) written to our internal ring buffer. */ 153 151 uint64_t cbWritten; 154 152 /** Total amount (in bytes) played (to the DirectSound buffer). */ 155 uint64_t cbPlayed; 156 /** Flag indicating whether playback was (re)started. */ 157 bool fFirstPlayback; 158 /** Timestamp (in ms) of When the last playback has happened. */ 159 uint64_t tsLastPlayMs; 153 uint64_t cbTransferred; 154 /** Flag indicating whether playback was just (re)started. */ 155 bool fFirstTransfer; 156 /** How much (in bytes) the last transfer from the internal buffer 157 * to the DirectSound buffer was. */ 158 uint32_t cbLastTransferred; 159 /** Timestamp (in ms) of the last transfer from the internal buffer 160 * to the DirectSound buffer. */ 161 uint64_t tsLastTransferred; 160 162 /** Number of buffer underruns happened. Used for logging. */ 161 163 uint8_t cUnderruns; 162 164 } Out; 163 165 }; 166 struct 167 { 168 uint64_t tsLastTransferredMs; 169 } Dbg; 164 170 } DSOUNDSTREAM, *PDSOUNDSTREAM; 165 171 … … 194 200 PFNPDMHOSTAUDIOCALLBACK pfnCallback; 195 201 #endif 196 /** Stopped indicator. */197 bool fStopped;198 /** Shutdown indicator. */199 bool fShutdown;200 /** Notification thread. */201 RTTHREAD Thread;202 /** Array of events to wait for in notification thread. */203 HANDLE aEvents[VBOX_DSOUND_MAX_EVENTS];204 202 /** Pointer to the input stream. */ 205 203 PDSOUNDSTREAM pDSStrmIn; … … 240 238 *********************************************************************************************************************************/ 241 239 static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB); 240 static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS); 242 241 static HRESULT directSoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush); 243 242 static HRESULT directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush); … … 245 244 static void dsoundDeviceRemove(PDSOUNDDEV pDev); 246 245 static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg); 247 248 static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown);249 246 250 247 static void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis); … … 292 289 AssertPtrReturn(pdwFree, VERR_INVALID_POINTER); 293 290 294 Assert(pStreamDS-> pCfg->enmDir == PDMAUDIODIR_OUT); /* Paranoia. */291 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT); /* Paranoia. */ 295 292 296 293 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB; … … 601 598 602 599 RTCritSectLeave(&pThis->CritSect); 603 604 int rc2 = dsoundNotifyThread(pThis, false /* fShutdown */);605 AssertRC(rc2);606 600 } 607 601 … … 679 673 * of copying own buffer data to our secondary's Direct Sound buffer. 680 674 */ 681 bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE; 682 bd.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY; 683 684 bd.dwBufferBytes = DrvAudioHlpMsToBytes(&pCfgReq->Props, pThis->Cfg.msLatencyOut); 685 686 DSLOG(("DSound: Playback buffer is %ld bytes\n", bd.dwBufferBytes)); 675 bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE; 676 bd.dwBufferBytes = DrvAudioHlpFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize); 677 678 DSLOG(("DSound: Requested playback buffer is %RU64ms (%ld bytes)\n", 679 pCfgReq->Backend.cfBufferSize, DrvAudioHlpBytesToMs(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes)); 687 680 688 681 hr = IDirectSound8_CreateSoundBuffer(pThis->pDS, &bd, &pDSB, NULL); … … 724 717 break; 725 718 } 719 720 DSLOG(("DSound: Acquired playback buffer is %RU64ms (%ld bytes)\n", 721 DrvAudioHlpBytesToMs(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes)); 726 722 727 723 DSLOG(("DSound: Acquired playback format:\n" … … 756 752 pStreamDS->cbBufSize = bc.dwBufferBytes; 757 753 758 RTCritSectEnter(&pThis->CritSect); 759 760 rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize * 2 /* Double buffering */); 754 rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize) * 2; /* Use "double buffering" */ 761 755 AssertRC(rc); 762 756 763 /* 764 * Install notification. 765 */ 766 LPDIRECTSOUNDNOTIFY8 pNotify; 767 hr = IDirectSoundNotify_QueryInterface(pStreamDS->Out.pDSB, IID_IDirectSoundNotify8, (PVOID *)&pNotify); 768 if (SUCCEEDED(hr)) 769 { 770 DSBPOSITIONNOTIFY dsPosNotify[3]; 771 RT_ZERO(dsPosNotify); 772 773 dsPosNotify[0].dwOffset = 0; 774 dsPosNotify[0].hEventNotify = pThis->aEvents[DSOUNDEVENT_OUTPUT]; 775 776 dsPosNotify[1].dwOffset = float(pStreamDS->cbBufSize * 0.3); 777 dsPosNotify[1].hEventNotify = pThis->aEvents[DSOUNDEVENT_OUTPUT]; 778 779 dsPosNotify[2].dwOffset = float(pStreamDS->cbBufSize * 0.6); 780 dsPosNotify[2].hEventNotify = pThis->aEvents[DSOUNDEVENT_OUTPUT]; 781 782 hr = IDirectSoundNotify_SetNotificationPositions(pNotify, RT_ELEMENTS(dsPosNotify), dsPosNotify); 783 if (FAILED(hr)) 784 DSLOGREL(("DSound: Setting playback position notification failed with %Rhrc\n", hr)); 785 786 IDirectSoundNotify_Release(pNotify); 787 788 pThis->pDSStrmOut = pStreamDS; 789 } 790 else 791 DSLOGREL(("DSound: Querying interface for position notification failed with %Rhrc\n", hr)); 792 793 RTCritSectLeave(&pThis->CritSect); 794 795 pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize); 757 pThis->pDSStrmOut = pStreamDS; 758 759 pCfgAcq->Backend.cfBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize); 760 pCfgAcq->Backend.cfPeriod = pCfgAcq->Backend.cfBufferSize / 2; 761 pCfgAcq->Backend.cfPreBuf = pCfgAcq->Backend.cfBufferSize; 796 762 797 763 } while (0); … … 807 773 AssertPtrReturnVoid(pStreamDS); 808 774 809 AssertPtr(pStreamDS->pCfg); 810 PPDMAUDIOPCMPROPS pProps = &pStreamDS->pCfg->Props; 775 PPDMAUDIOPCMPROPS pProps = &pStreamDS->Cfg.Props; 811 776 812 777 HRESULT hr = IDirectSoundBuffer_SetCurrentPosition(pStreamDS->Out.pDSB, 0 /* Position */); … … 831 796 DSLOGREL(("DSound: Re-fetching current position when clearing buffer failed with %Rhrc\n", hr)); 832 797 } 798 } 799 800 /** 801 * Transfers audio data from the internal buffer to the DirectSound playback instance. 802 * Due to internal accounting and querying DirectSound, this function knows how much it can transfer at once. 803 * 804 * @return IPRT status code. 805 * @param pThis Host audio driver instance. 806 */ 807 static int dsoundPlayTransfer(PDRVHOSTDSOUND pThis) 808 { 809 PDSOUNDSTREAM pStreamDS = pThis->pDSStrmOut; 810 811 if ( !pStreamDS 812 || !pStreamDS->fEnabled) 813 { 814 return VINF_SUCCESS; 815 } 816 817 uint32_t cbTransferred = 0; 818 819 PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf; 820 AssertPtr(pCircBuf); 821 822 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB; 823 AssertPtr(pDSB); 824 825 int rc = VINF_SUCCESS; 826 827 DWORD offPlayCursor, offWriteCursor; 828 HRESULT hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &offPlayCursor, &offWriteCursor); 829 if (FAILED(hr)) 830 { 831 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 832 return rc; 833 } 834 835 DWORD cbFree, cbRemaining; 836 if (pStreamDS->Out.fFirstTransfer) 837 { 838 cbRemaining = 0; 839 cbFree = pStreamDS->cbBufSize; 840 } 841 else 842 { 843 cbFree = dsoundRingDistance(offPlayCursor, pStreamDS->Out.offWritePos, pStreamDS->cbBufSize); 844 cbRemaining = dsoundRingDistance(pStreamDS->Out.offWritePos, offPlayCursor, pStreamDS->cbBufSize); 845 } 846 847 uint32_t cbAvail = (uint32_t)RTCircBufUsed(pCircBuf); 848 uint32_t cbToTransfer = RT_MIN(cbFree, cbAvail); 849 850 #ifdef LOG_ENABLED 851 if (!pStreamDS->Dbg.tsLastTransferredMs) 852 pStreamDS->Dbg.tsLastTransferredMs = RTTimeMilliTS(); 853 Log3Func(("tsLastTransferredMs=%RU64ms, cbAvail=%RU32, cbFree=%RU32 -> cbToTransfer=%RU32\n", 854 RTTimeMilliTS() - pStreamDS->Dbg.tsLastTransferredMs, cbAvail, cbFree, cbToTransfer)); 855 pStreamDS->Dbg.tsLastTransferredMs = RTTimeMilliTS(); 856 #endif 857 858 #if 0 859 if (cbToTransfer > cbAvail) /* Paranoia. */ 860 cbToTransfer = cbAvail; 861 862 if (cbToTransfer > cbFree) 863 cbToTransfer = cbFree; 864 #endif 865 866 while (cbToTransfer) 867 { 868 DWORD cb1 = 0; 869 DWORD cb2 = 0; 870 871 void *pvBuf; 872 size_t cbBuf; 873 RTCircBufAcquireReadBlock(pCircBuf, cbToTransfer, &pvBuf, &cbBuf); 874 875 if (cbBuf) 876 { 877 PVOID pv1, pv2; 878 hr = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, (DWORD)cbBuf, 879 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */); 880 if (FAILED(hr)) 881 { 882 rc = VERR_ACCESS_DENIED; 883 break; 884 } 885 886 AssertPtr(pv1); 887 Assert(cb1); 888 889 memcpy(pv1, pvBuf, cb1); 890 891 if (pv2 && cb2) /* Buffer wrap-around? Write second part. */ 892 memcpy(pv2, (uint8_t *)pvBuf + cb1, cb2); 893 894 directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2); 895 896 pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cb1 + cb2) % pStreamDS->cbBufSize; 897 898 Assert(cbToTransfer >= cbBuf); 899 cbToTransfer -= (uint32_t)cbBuf; 900 901 cbTransferred += cb1 + cb2; 902 } 903 904 RTCircBufReleaseReadBlock(pCircBuf, cb1 + cb2); 905 } 906 907 pStreamDS->Out.cbTransferred += cbTransferred; 908 909 if ( pStreamDS->Out.fFirstTransfer 910 && pStreamDS->Out.cbTransferred >= DrvAudioHlpFramesToBytes(&pStreamDS->Cfg.Props, pStreamDS->Cfg.Backend.cfPreBuf)) 911 { 912 hr = directSoundPlayStart(pThis, pStreamDS); 913 if (SUCCEEDED(hr)) 914 { 915 DSLOG(("DSound: Started playing output\n")); 916 pStreamDS->Out.fFirstTransfer = false; 917 } 918 else 919 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 920 } 921 922 cbAvail = (uint32_t)RTCircBufUsed(pCircBuf); 923 if ( !cbAvail 924 && cbTransferred) 925 { 926 pStreamDS->Out.cbLastTransferred = cbTransferred; 927 pStreamDS->Out.tsLastTransferred = RTTimeMilliTS(); 928 } 929 930 LogFlowFunc(("cbTransferred=%RU32, cbAvail=%RU32, rc=%Rrc\n", cbTransferred, cbAvail, rc)); 931 return rc; 833 932 } 834 933 … … 908 1007 909 1008 910 static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1009 /** 1010 * Resets the state of a DirectSound stream. 1011 * 1012 * @return HRESULT 1013 * @param pThis Host audio driver instance. 1014 * @param pStreamDS Stream to reset state for. 1015 */ 1016 static HRESULT directSoundPlayReset(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 911 1017 { 912 1018 AssertPtrReturn(pThis, E_POINTER); … … 919 1025 * once enough audio output data is available. */ 920 1026 921 pStreamDS->Out.fFirstPlayback = true; 922 pStreamDS->Out.cUnderruns = 0; 923 924 DSLOG(("DSound: Playback started\n")); 1027 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT) 1028 { 1029 pStreamDS->Out.fFirstTransfer = true; 1030 pStreamDS->Out.cUnderruns = 0; 1031 1032 pStreamDS->Out.cbLastTransferred = 0; 1033 pStreamDS->Out.tsLastTransferred = 0; 1034 1035 pStreamDS->Out.cbTransferred = 0; 1036 pStreamDS->Out.cbWritten = 0; 1037 1038 pStreamDS->Out.offPlayCursorLastPending = 0; 1039 pStreamDS->Out.offPlayCursorLastPlayed = 0; 1040 } 925 1041 926 1042 return S_OK; 927 1043 } 1044 1045 1046 /** 1047 * Starts playing a DirectSound stream. 1048 * 1049 * @return HRESULT 1050 * @param pThis Host audio driver instance. 1051 * @param pStreamDS Stream to start playing. 1052 */ 1053 static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS) 1054 { 1055 HRESULT hr = S_OK; 1056 1057 DWORD fFlags = DSCBSTART_LOOPING; 1058 1059 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++) 1060 { 1061 hr = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, fFlags); 1062 if ( SUCCEEDED(hr) 1063 || hr != DSERR_BUFFERLOST) 1064 break; 1065 else 1066 { 1067 LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n")); 1068 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB); 1069 } 1070 } 1071 1072 return hr; 1073 } 1074 928 1075 929 1076 /* … … 1124 1271 bd.dwSize = sizeof(bd); 1125 1272 bd.lpwfxFormat = &wfx; 1126 bd.dwBufferBytes = DrvAudioHlpMsToBytes(&pCfgReq->Props, pThis->Cfg.msLatencyIn); 1127 1128 DSLOG(("DSound: Capture buffer is %ld bytes\n", bd.dwBufferBytes)); 1273 bd.dwBufferBytes = DrvAudioHlpFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize); 1274 1275 DSLOG(("DSound: Requested capture buffer is %RU64ms (%ld bytes)\n", 1276 pCfgReq->Backend.cfBufferSize, DrvAudioHlpBytesToMs(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes)); 1129 1277 1130 1278 LPDIRECTSOUNDCAPTUREBUFFER pDSCB; … … 1177 1325 break; 1178 1326 } 1327 1328 DSLOG(("DSound: Acquired capture buffer is %RU64ms (%ld bytes)\n", 1329 DrvAudioHlpBytesToMs(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes)); 1179 1330 1180 1331 DSLOG(("DSound: Capture format:\n" … … 1206 1357 pStreamDS->cbBufSize = bc.dwBufferBytes; 1207 1358 1208 rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize * 2 /* Double buffering */);1359 rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize) * 2; /* Use "double buffering". */ 1209 1360 AssertRC(rc); 1210 1361 1211 /* 1212 * Install notification. 1213 */ 1214 LPDIRECTSOUNDNOTIFY8 pNotify; 1215 hr = IDirectSoundNotify_QueryInterface(pStreamDS->In.pDSCB, IID_IDirectSoundNotify8, (PVOID *)&pNotify); 1216 if (SUCCEEDED(hr)) 1217 { 1218 DSBPOSITIONNOTIFY dsPosNotify[3]; 1219 RT_ZERO(dsPosNotify); 1220 1221 dsPosNotify[0].dwOffset = 0; 1222 dsPosNotify[0].hEventNotify = pThis->aEvents[DSOUNDEVENT_INPUT]; 1223 1224 dsPosNotify[1].dwOffset = float(pStreamDS->cbBufSize * 0.3); 1225 dsPosNotify[1].hEventNotify = pThis->aEvents[DSOUNDEVENT_INPUT]; 1226 1227 dsPosNotify[2].dwOffset = float(pStreamDS->cbBufSize * 0.6); 1228 dsPosNotify[2].hEventNotify = pThis->aEvents[DSOUNDEVENT_INPUT]; 1229 1230 hr = IDirectSoundNotify_SetNotificationPositions(pNotify, 3 /* Count */, dsPosNotify); 1231 if (FAILED(hr)) 1232 DSLOGREL(("DSound: Setting capture position notification failed with %Rhrc\n", hr)); 1233 1234 IDirectSoundNotify_Release(pNotify); 1235 1236 pThis->pDSStrmIn = pStreamDS; 1237 } 1238 1239 pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize); 1362 pThis->pDSStrmIn = pStreamDS; 1363 1364 pCfgAcq->Backend.cfBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize); 1240 1365 1241 1366 } while (0); … … 1546 1671 pStreamDS->Out.offPlayCursorLastPending = 0; 1547 1672 pStreamDS->Out.cbWritten = 0; 1548 pStreamDS->Out.cbPlayed = 0; 1549 pStreamDS->Out.fFirstPlayback = true; 1550 pStreamDS->Out.tsLastPlayMs = 0; 1673 pStreamDS->Out.cbTransferred = 0; 1674 pStreamDS->Out.fFirstTransfer = true; 1675 pStreamDS->Out.cbLastTransferred = 0; 1676 pStreamDS->Out.tsLastTransferred = 0; 1551 1677 1552 1678 int rc = VINF_SUCCESS; … … 1572 1698 case PDMAUDIOSTREAMCMD_ENABLE: 1573 1699 { 1700 hr = directSoundPlayReset(pThis, pStreamDS); 1701 if (FAILED(hr)) 1702 rc = VERR_NOT_SUPPORTED; /** @todo Fix this. */ 1703 break; 1704 } 1705 1706 case PDMAUDIOSTREAMCMD_RESUME: 1707 { 1574 1708 hr = directSoundPlayStart(pThis, pStreamDS); 1575 1709 if (FAILED(hr)) … … 1578 1712 } 1579 1713 1580 case PDMAUDIOSTREAMCMD_RESUME:1581 {1582 hr = directSoundPlayStart(pThis, pStreamDS);1583 if (SUCCEEDED(hr))1584 {1585 BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_OUTPUT]);1586 RT_NOREF(fRc);1587 Assert(fRc);1588 }1589 1590 if (FAILED(hr))1591 rc = VERR_NOT_SUPPORTED; /** @todo Fix this. */1592 break;1593 }1594 1595 1714 case PDMAUDIOSTREAMCMD_DISABLE: 1596 1715 case PDMAUDIOSTREAMCMD_PAUSE: … … 1602 1721 } 1603 1722 1723 case PDMAUDIOSTREAMCMD_DRAIN: 1724 { 1725 /* Make sure we transferred everything. */ 1726 rc = dsoundPlayTransfer(pThis); 1727 if ( RT_SUCCESS(rc) 1728 && pStreamDS->Out.fFirstTransfer) 1729 { 1730 /* If this was the first transfer ever for this stream, make sure to also play the (short) audio data. */ 1731 DSLOG(("DSound: Started playing output (short sound)\n")); 1732 1733 pStreamDS->Out.fFirstTransfer = false; 1734 pStreamDS->Out.cbLastTransferred = pStreamDS->Out.cbTransferred; /* All transferred audio data must be played. */ 1735 pStreamDS->Out.tsLastTransferred = RTTimeMilliTS(); 1736 1737 hr = directSoundPlayStart(pThis, pStreamDS); 1738 if (FAILED(hr)) 1739 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1740 } 1741 break; 1742 } 1743 1604 1744 default: 1605 { 1606 AssertMsgFailed(("Invalid command: %ld\n", enmStreamCmd)); 1607 rc = VERR_INVALID_PARAMETER; 1745 rc = VERR_NOT_SUPPORTED; 1608 1746 break; 1609 }1610 1747 } 1611 1748 … … 1658 1795 1659 1796 Assert(cbWrittenTotal <= cxBuf); 1797 Assert(cbWrittenTotal == cxBuf); 1660 1798 1661 1799 pStreamDS->Out.cbWritten += cbWrittenTotal; 1662 1800 1663 if ( pStreamDS->Out.fFirstPlayback 1664 && pStreamDS->Out.cbWritten >= pStreamDS->cbBufSize) 1665 { 1666 BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_OUTPUT]); 1667 RT_NOREF(fRc); 1668 Assert(fRc); 1669 } 1801 rc = dsoundPlayTransfer(pThis); 1802 AssertRC(rc); 1670 1803 1671 1804 if (RT_SUCCESS(rc)) … … 1740 1873 { 1741 1874 PDMAUDIOSTREAMCFG CfgAcq; 1742 hr = directSoundCaptureOpen(pThis, pStreamDS, pStreamDS->pCfg /* pCfgReq */, &CfgAcq);1875 hr = directSoundCaptureOpen(pThis, pStreamDS, &pStreamDS->Cfg /* pCfgReq */, &CfgAcq); 1743 1876 if (SUCCEEDED(hr)) 1744 1877 { 1745 DrvAudioHlpStreamCfgFree(pStreamDS->pCfg); 1746 1747 pStreamDS->pCfg = DrvAudioHlpStreamCfgDup(&CfgAcq); 1748 AssertPtr(pStreamDS->pCfg); 1878 rc = DrvAudioHlpStreamCfgCopy(&pStreamDS->Cfg, &CfgAcq); 1879 if (RT_FAILURE(rc)) 1880 break; 1749 1881 1750 1882 /** @todo What to do if the format has changed? */ … … 1777 1909 1778 1910 default: 1779 { 1780 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd)); 1781 rc = VERR_INVALID_PARAMETER; 1911 rc = VERR_NOT_SUPPORTED; 1782 1912 break; 1783 }1784 1913 } 1785 1914 … … 1859 1988 } 1860 1989 1861 static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown) 1862 { 1863 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 1864 1865 if (fShutdown) 1866 { 1867 LogFlowFunc(("Shutting down thread ...\n")); 1868 pThis->fShutdown = fShutdown; 1869 } 1870 1871 /* Set the notification event so that the thread is being notified. */ 1872 BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_NOTIFY]); 1873 RT_NOREF(fRc); 1874 Assert(fRc); 1875 1876 return VINF_SUCCESS; 1877 } 1878 1879 1880 static DECLCALLBACK(int) dsoundThread(RTTHREAD hThreadSelf, void *pvUser) 1881 { 1882 PDRVHOSTDSOUND pThis = (PDRVHOSTDSOUND)pvUser; 1883 AssertPtr(pThis); 1990 /** 1991 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown} 1992 */ 1993 void drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface) 1994 { 1995 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface); 1884 1996 1885 1997 LogFlowFuncEnter(); 1886 1998 1887 /* Let caller know that we're done initializing, regardless of the result. */ 1888 int rc = RTThreadUserSignal(hThreadSelf); 1889 AssertRC(rc); 1890 1891 for (;;) 1892 { 1893 RTCritSectEnter(&pThis->CritSect); 1894 1895 HANDLE aEvents[VBOX_DSOUND_MAX_EVENTS]; 1896 DWORD cEvents = 0; 1897 for (uint8_t i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++) 1898 { 1899 if (pThis->aEvents[i]) 1900 aEvents[cEvents++] = pThis->aEvents[i]; 1901 } 1902 Assert(cEvents); 1903 1904 RTCritSectLeave(&pThis->CritSect); 1905 1906 DWORD dwObj = WaitForMultipleObjects(cEvents, aEvents, FALSE /* bWaitAll */, INFINITE); 1907 1908 RTCritSectEnter(&pThis->CritSect); 1909 1910 switch (dwObj) 1911 { 1912 case WAIT_FAILED: 1913 { 1914 rc = VERR_CANCELLED; 1915 break; 1916 } 1917 1918 case WAIT_TIMEOUT: 1919 { 1920 rc = VERR_TIMEOUT; 1921 break; 1922 } 1923 1924 case WAIT_OBJECT_0: 1925 case WAIT_OBJECT_0 + 1: 1926 case WAIT_OBJECT_0 + 2: 1927 case WAIT_OBJECT_0 + 3: 1928 case WAIT_OBJECT_0 + 4: 1929 { 1930 dwObj -= WAIT_OBJECT_0; 1931 1932 if (dwObj == DSOUNDEVENT_NOTIFY) 1933 { 1934 Log3Func(("Notify\n")); 1935 } 1936 else if (dwObj == DSOUNDEVENT_INPUT) 1937 { 1938 PDSOUNDSTREAM pStreamDS = pThis->pDSStrmIn; 1939 1940 if ( !pStreamDS 1941 || !pStreamDS->fEnabled) 1942 { 1943 Log3Func(("Skipping capture\n")); 1944 break; 1945 } 1946 1947 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB = pStreamDS->In.pDSCB; 1948 AssertPtr(pDSCB); 1949 1950 DWORD offCaptureCursor; 1951 HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &offCaptureCursor); 1952 if (FAILED(hr)) 1953 break; 1954 1955 DWORD cbUsed = dsoundRingDistance(offCaptureCursor, pStreamDS->In.offReadPos, pStreamDS->cbBufSize); 1956 1957 PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf; 1958 AssertPtr(pCircBuf); 1959 1960 uint32_t cbFree = (uint32_t)RTCircBufFree(pCircBuf); 1961 if ( !cbFree 1962 || pStreamDS->In.cOverruns < 32) /** @todo Make this configurable. */ 1963 { 1964 DSLOG(("DSound: Warning: Capture buffer full, skipping to record data (%RU32 bytes)\n", cbUsed)); 1965 pStreamDS->In.cOverruns++; 1966 } 1967 1968 DWORD cbToCapture = RT_MIN(cbUsed, cbFree); 1969 1970 Log3Func(("cbUsed=%ld, cbToCapture=%ld\n", cbUsed, cbToCapture)); 1971 1972 while (cbToCapture) 1973 { 1974 void *pvBuf; 1975 size_t cbBuf; 1976 RTCircBufAcquireWriteBlock(pCircBuf, cbToCapture, &pvBuf, &cbBuf); 1977 1978 if (cbBuf) 1979 { 1980 PVOID pv1, pv2; 1981 DWORD cb1, cb2; 1982 hr = directSoundCaptureLock(pStreamDS, pStreamDS->In.offReadPos, (DWORD)cbBuf, 1983 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */); 1984 if (FAILED(hr)) 1985 break; 1986 1987 AssertPtr(pv1); 1988 Assert(cb1); 1989 1990 memcpy(pvBuf, pv1, cb1); 1991 1992 if (pv2 && cb2) /* Buffer wrap-around? Write second part. */ 1993 memcpy((uint8_t *)pvBuf + cb1, pv2, cb2); 1994 1995 directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2); 1996 1997 pStreamDS->In.offReadPos = (pStreamDS->In.offReadPos + cb1 + cb2) % pStreamDS->cbBufSize; 1998 1999 Assert(cbToCapture >= cbBuf); 2000 cbToCapture -= (uint32_t)cbBuf; 2001 } 2002 2003 #ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA 2004 if (cbBuf) 2005 { 2006 RTFILE fh; 2007 int rc2 = RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "dsoundCapture.pcm", 2008 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 2009 if (RT_SUCCESS(rc2)) 2010 { 2011 RTFileWrite(fh, pvBuf, cbBuf, NULL); 2012 RTFileClose(fh); 2013 } 2014 } 2015 #endif 2016 RTCircBufReleaseWriteBlock(pCircBuf, cbBuf); 2017 } 2018 } 2019 else if (dwObj == DSOUNDEVENT_OUTPUT) 2020 { 2021 PDSOUNDSTREAM pStreamDS = pThis->pDSStrmOut; 2022 2023 if ( !pStreamDS 2024 || !pStreamDS->fEnabled) 2025 { 2026 Log3Func(("Skipping playback\n")); 2027 break; 2028 } 2029 2030 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB; 2031 AssertPtr(pDSB); 2032 2033 HRESULT hr; 2034 2035 DWORD offPlayCursor, offWriteCursor; 2036 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &offPlayCursor, &offWriteCursor); 2037 if (FAILED(hr)) 2038 break; 2039 2040 DWORD cbFree, cbRemaining; 2041 if (pStreamDS->Out.fFirstPlayback) 2042 { 2043 cbRemaining = 0; 2044 cbFree = dsoundRingDistance(pStreamDS->Out.offWritePos, 0, pStreamDS->cbBufSize); 2045 } 2046 else 2047 { 2048 cbFree = dsoundRingDistance(offPlayCursor, pStreamDS->Out.offWritePos, pStreamDS->cbBufSize); 2049 cbRemaining = dsoundRingDistance(pStreamDS->Out.offWritePos, offPlayCursor, pStreamDS->cbBufSize); 2050 } 2051 2052 PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf; 2053 AssertPtr(pCircBuf); 2054 2055 DWORD cbUsed = (uint32_t)RTCircBufUsed(pCircBuf); 2056 DWORD cbToPlay = RT_MIN(cbFree, cbUsed); 2057 2058 if ( !cbUsed 2059 && pStreamDS->Out.cbWritten 2060 && pStreamDS->Out.cUnderruns < 32) /** @todo Make this configurable. */ 2061 { 2062 //DSLOG(("DSound: Warning: No more playback data available within time (%RU32 bytes free)\n", cbFree)); 2063 Log3Func(("Underrun (cbFree=%ld, cbRemaining=%ld, cbUsed=%ld, cbToPlay=%ld, offPlay=%ld, offWrite=%ld)\n", 2064 cbFree, cbRemaining, cbUsed, cbToPlay, offPlayCursor, offWriteCursor)); 2065 pStreamDS->Out.cUnderruns++; 2066 } 2067 2068 while (cbToPlay) 2069 { 2070 void *pvBuf; 2071 size_t cbBuf; 2072 RTCircBufAcquireReadBlock(pCircBuf, cbToPlay, &pvBuf, &cbBuf); 2073 2074 if (cbBuf) 2075 { 2076 PVOID pv1, pv2; 2077 DWORD cb1, cb2; 2078 hr = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, (DWORD)cbBuf, 2079 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */); 2080 if (FAILED(hr)) 2081 break; 2082 2083 AssertPtr(pv1); 2084 Assert(cb1); 2085 2086 memcpy(pv1, pvBuf, cb1); 2087 2088 if (pv2 && cb2) /* Buffer wrap-around? Write second part. */ 2089 memcpy(pv2, (uint8_t *)pvBuf + cb1, cb2); 2090 2091 directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2); 2092 2093 pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cb1 + cb2) % pStreamDS->cbBufSize; 2094 2095 Assert(cbToPlay >= cbBuf); 2096 cbToPlay -= (uint32_t)cbBuf; 2097 2098 pStreamDS->Out.cbPlayed += cb1 + cb2; 2099 } 2100 2101 RTCircBufReleaseReadBlock(pCircBuf, cbBuf); 2102 } 2103 2104 if ( pStreamDS->Out.fFirstPlayback 2105 && RTCircBufUsed(pCircBuf) >= pStreamDS->cbBufSize) 2106 { 2107 DWORD fFlags = DSCBSTART_LOOPING; 2108 2109 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++) 2110 { 2111 hr = IDirectSoundBuffer8_Play(pDSB, 0, 0, fFlags); 2112 if ( SUCCEEDED(hr) 2113 || hr != DSERR_BUFFERLOST) 2114 break; 2115 else 2116 { 2117 LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n")); 2118 directSoundPlayRestore(pThis, pDSB); 2119 } 2120 } 2121 2122 if (SUCCEEDED(hr)) 2123 { 2124 DSLOG(("DSound: Started playing output\n")); 2125 pStreamDS->Out.fFirstPlayback = false; 2126 } 2127 } 2128 } 2129 break; 2130 } 2131 2132 default: 2133 AssertFailed(); 2134 break; 2135 } 2136 2137 RTCritSectLeave(&pThis->CritSect); 2138 2139 if (pThis->fShutdown) 2140 break; 2141 2142 } /* for */ 2143 2144 pThis->fStopped = true; 2145 2146 LogFlowFunc(("Exited with fShutdown=%RTbool, rc=%Rrc\n", pThis->fShutdown, rc)); 2147 return rc; 2148 } 2149 2150 /** 2151 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown} 2152 */ 2153 void drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface) 2154 { 2155 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface); 2156 2157 LogFlowFuncEnter(); 2158 2159 int rc = dsoundNotifyThread(pThis, true /* fShutdown */); 2160 AssertRC(rc); 2161 2162 int rcThread; 2163 rc = RTThreadWait(pThis->Thread, 15 * 1000 /* 15s timeout */, &rcThread); 2164 LogFlowFunc(("rc=%Rrc, rcThread=%Rrc\n", rc, rcThread)); 2165 2166 Assert(pThis->fStopped); 2167 2168 for (int i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++) 2169 { 2170 if (pThis->aEvents[i]) 2171 CloseHandle(pThis->aEvents[i]); 2172 } 1999 RT_NOREF(pThis); 2173 2000 2174 2001 LogFlowFuncLeave(); … … 2192 2019 IDirectSound_Release(pDirectSound); 2193 2020 2194 /* Create notification events. */ 2195 for (int i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++) 2196 { 2197 pThis->aEvents[i] = CreateEvent(NULL /* Security attribute */, 2198 FALSE /* bManualReset */, FALSE /* bInitialState */, 2199 NULL /* lpName */); 2200 Assert(pThis->aEvents[i] != NULL); 2201 } 2202 2203 /* Start notification thread. */ 2204 rc = RTThreadCreate(&pThis->Thread, dsoundThread, 2205 pThis /*pvUser*/, 0 /*cbStack*/, 2206 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "dsoundNtfy"); 2207 if (RT_SUCCESS(rc)) 2208 { 2209 /* Wait for the thread to initialize. */ 2210 rc = RTThreadUserWait(pThis->Thread, RT_MS_1MIN); 2211 if (RT_FAILURE(rc)) 2212 DSLOGREL(("DSound: Waiting for thread to initialize failed with rc=%Rrc\n", rc)); 2213 } 2214 else 2215 DSLOGREL(("DSound: Creating thread failed with rc=%Rrc\n", rc)); 2021 rc = VINF_SUCCESS; 2216 2022 2217 2023 dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, DSOUNDENUMCBFLAGS_LOG /* fEnum */); … … 2249 2055 static int dsoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg) 2250 2056 { 2251 CFGMR3QueryUIntDef(pCfg, "LatencyMsIn", &pThis->Cfg.msLatencyIn, DRV_DSOUND_DEFAULT_LATENCY_MS_IN);2252 CFGMR3QueryUIntDef(pCfg, "LatencyMsOut", &pThis->Cfg.msLatencyOut, DRV_DSOUND_DEFAULT_LATENCY_MS_OUT);2253 2254 2057 pThis->Cfg.pGuidPlay = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->Cfg.uuidPlay); 2255 2058 pThis->Cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn", &pThis->Cfg.uuidCapture); 2256 2059 2257 DSLOG(("DSound: Configuration: Input latency = %ums, Output latency = %ums, DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n", 2258 pThis->Cfg.msLatencyIn, 2259 pThis->Cfg.msLatencyOut, 2060 DSLOG(("DSound: Configuration: DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n", 2260 2061 &pThis->Cfg.uuidPlay, 2261 2062 &pThis->Cfg.uuidCapture)); … … 2297 2098 if (RT_SUCCESS(rc)) 2298 2099 { 2299 pStreamDS->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq); 2300 if (pStreamDS->pCfg) 2301 { 2100 rc = DrvAudioHlpStreamCfgCopy(&pStreamDS->Cfg, pCfgAcq); 2101 if (RT_SUCCESS(rc)) 2302 2102 rc = RTCritSectInit(&pStreamDS->CritSect); 2303 }2304 else2305 rc = VERR_NO_MEMORY;2306 2103 } 2307 2104 … … 2320 2117 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2321 2118 2322 if (!pStreamDS->pCfg) /* Not (yet) configured? Skip. */2323 return VINF_SUCCESS;2324 2325 2119 int rc; 2326 if (pStreamDS-> pCfg->enmDir == PDMAUDIODIR_IN)2120 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN) 2327 2121 rc = dsoundDestroyStreamIn(pThis, pStreamDS); 2328 2122 else … … 2332 2126 { 2333 2127 rc = RTCritSectDelete(&pStreamDS->CritSect); 2334 2335 DrvAudioHlpStreamCfgFree(pStreamDS->pCfg);2336 pStreamDS->pCfg = NULL;2337 2128 } 2338 2129 … … 2352 2143 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2353 2144 2354 if (!pStreamDS->pCfg) /* Not (yet) configured? Skip. */2355 return VINF_SUCCESS;2356 2357 2145 int rc; 2358 if (pStreamDS-> pCfg->enmDir == PDMAUDIODIR_IN)2146 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN) 2359 2147 rc = dsoundControlStreamIn(pThis, pStreamDS, enmStreamCmd); 2360 2148 else … … 2409 2197 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2410 2198 2411 if (pStreamDS-> pCfg->enmDir == PDMAUDIODIR_OUT)2199 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT) 2412 2200 { 2413 2201 uint32_t cbPending = 0; … … 2421 2209 if (!cbPending) 2422 2210 { 2423 const uint64_t tsNowMs = RTTimeMilliTS(); 2424 if (pStreamDS->Out.tsLastPlayMs == 0) 2425 pStreamDS->Out.tsLastPlayMs = tsNowMs; 2426 2427 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface); 2428 2429 const uint64_t diffLastPlayMs = tsNowMs - pStreamDS->Out.tsLastPlayMs; 2430 const uint64_t msThreshold = pThis->Cfg.msLatencyOut; 2431 2432 Log2Func(("diffLastPlayMs=%RU64ms\n", diffLastPlayMs)); 2433 2434 cbPending = (diffLastPlayMs >= msThreshold) ? 0 : 1; 2435 } 2211 const uint64_t diffLastTransferredMs = RTTimeMilliTS() - pStreamDS->Out.tsLastTransferred; 2212 const uint64_t uLastTranserredChunkMs = DrvAudioHlpBytesToMs(&pStreamDS->Cfg.Props, pStreamDS->Out.cbLastTransferred); 2213 if ( uLastTranserredChunkMs 2214 && diffLastTransferredMs < uLastTranserredChunkMs) 2215 cbPending = 1; 2216 2217 Log3Func(("diffLastTransferredMs=%RU64ms, uLastTranserredChunkMs=%RU64ms (%RU32 bytes) -> cbPending=%RU32\n", 2218 diffLastTransferredMs, uLastTranserredChunkMs, pStreamDS->Out.cbLastTransferred, cbPending)); 2219 } 2220 else 2221 Log3Func(("cbPending=%RU32\n", cbPending)); 2436 2222 2437 2223 return cbPending; … … 2452 2238 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2453 2239 2454 if (!pStreamDS->pCfg) /* Not (yet) configured? Skip. */2455 return PDMAUDIOSTREAMSTS_FLAG_NONE;2456 2457 2240 PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_INITIALIZED; 2458 if (pStreamDS-> pCfg->enmDir == PDMAUDIODIR_IN)2241 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN) 2459 2242 { 2460 2243 if (pStreamDS->fEnabled) … … 2623 2406 pThis->fEnabledIn = false; 2624 2407 pThis->fEnabledOut = false; 2625 pThis->fStopped = false;2626 pThis->fShutdown = false;2627 2628 RT_ZERO(pThis->aEvents);2629 2408 2630 2409 int rc = VINF_SUCCESS; -
trunk/src/VBox/Devices/Audio/DrvHostDebugAudio.cpp
r73350 r73370 120 120 121 121 if (pCfgAcq) 122 pCfgAcq->cFrameBufferHint = _1K; 122 { 123 pCfgAcq->Backend.cfPeriod = DrvAudioHlpMsToFrames(&pCfgReq->Props, 1000 /* ms */); 124 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */ 125 } 123 126 124 127 return VINF_SUCCESS; … … 159 162 { 160 163 if (pCfgAcq) 161 pCfgAcq->cFrameBufferHint = _1K; 164 { 165 pCfgAcq->Backend.cfPeriod = DrvAudioHlpBytesToFrames(&pCfgReq->Props, 50 /* ms */); 166 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */ 167 } 162 168 } 163 169 -
trunk/src/VBox/Devices/Audio/DrvHostNullAudio.cpp
r69119 r73370 174 174 static int nullCreateStreamIn(PNULLAUDIOSTREAM pStreamNull, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 175 175 { 176 RT_NOREF(pStreamNull, pCfgReq); 177 178 if (pCfgAcq) 179 pCfgAcq->cFrameBufferHint = _1K; 176 RT_NOREF(pStreamNull, pCfgReq, pCfgAcq); 180 177 181 178 return VINF_SUCCESS; … … 185 182 static int nullCreateStreamOut(PNULLAUDIOSTREAM pStreamNull, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 186 183 { 187 RT_NOREF(pStreamNull, pCfgReq); 188 189 if (pCfgAcq) 190 pCfgAcq->cFrameBufferHint = _1K; /** @todo Make this configurable. */ 184 RT_NOREF(pStreamNull, pCfgReq, pCfgAcq); 191 185 192 186 return VINF_SUCCESS; -
trunk/src/VBox/Devices/Audio/DrvHostOSSAudio.cpp
r73097 r73370 386 386 387 387 default: 388 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd)); 389 rc = VERR_INVALID_PARAMETER; 388 rc = VERR_NOT_SUPPORTED; 390 389 break; 391 390 } … … 650 649 } 651 650 652 uint32_t cSamples = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, ossAcq.cFragments * ossAcq.cbFragmentSize);653 if (!cSamples)654 rc = VERR_INVALID_PARAMETER;655 656 651 if (RT_SUCCESS(rc)) 657 652 { 658 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, cSamples);653 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, ossAcq.cFragments * ossAcq.cbFragmentSize); 659 654 void *pvBuf = RTMemAlloc(cbBuf); 660 655 if (!pvBuf) … … 668 663 pStreamOSS->cbBuf = cbBuf; 669 664 670 pCfgAcq->cFrameBufferHint = cSamples; 665 pCfgAcq->Backend.cfPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, ossAcq.cbFragmentSize); 666 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */ 667 /** @todo Pre-buffering required? */ 671 668 } 672 669 } … … 689 686 do 690 687 { 691 uint32_t cSamples;692 693 688 OSSAUDIOSTREAMCFG reqStream, obtStream; 694 689 … … 703 698 memcpy(&pCfgAcq->Props, &obtStream.Props, sizeof(PDMAUDIOPCMPROPS)); 704 699 705 cSamples = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, obtStream.cFragments * obtStream.cbFragmentSize);706 707 700 if (obtStream.cFragments * obtStream.cbFragmentSize & pStreamOSS->uAlign) 708 701 { … … 716 709 pStreamOSS->Out.fMMIO = false; 717 710 718 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, cSamples);711 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, obtStream.cFragments * obtStream.cbFragmentSize); 719 712 Assert(cbBuf); 720 713 … … 773 766 if (!pvBuf) 774 767 { 775 LogRel(("OSS: Failed allocating playback buffer with % RU32 samples (%zu bytes)\n", cSamples, cbBuf));768 LogRel(("OSS: Failed allocating playback buffer with %zu bytes\n", cbBuf)); 776 769 rc = VERR_NO_MEMORY; 777 770 break; … … 784 777 } 785 778 #endif 786 pCfgAcq->cFrameBufferHint = cSamples; 779 pCfgAcq->Backend.cfPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, obtStream.cbFragmentSize); 780 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering" */ 787 781 } 788 782 -
trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp
r73097 r73370 788 788 if (cbBuf) 789 789 { 790 pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, cbBuf); 790 pCfgAcq->Backend.cfPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.minreq); 791 pCfgAcq->Backend.cfBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.tlength); 792 pCfgAcq->Backend.cfPreBuf = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.prebuf); 791 793 792 794 pStreamPA->pDrv = pThis; … … 833 835 pCfgAcq->Props.uHz = pStreamPA->SampleSpec.rate; 834 836 pCfgAcq->Props.cChannels = pStreamPA->SampleSpec.channels; 835 pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, 836 RT_MIN(pStreamPA->BufAttr.fragsize * 10, pStreamPA->BufAttr.maxlength)); 837 838 pCfgAcq->Backend.cfPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.fragsize); 839 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use double buffering. */ 837 840 838 841 LogFlowFuncLeaveRC(rc); … … 1332 1335 1333 1336 default: 1334 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd)); 1335 rc = VERR_INVALID_PARAMETER; 1337 rc = VERR_NOT_SUPPORTED; 1336 1338 break; 1337 1339 } … … 1375 1377 1376 1378 default: 1377 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd)); 1378 rc = VERR_INVALID_PARAMETER; 1379 rc = VERR_NOT_SUPPORTED; 1379 1380 break; 1380 1381 } -
trunk/src/VBox/Devices/Audio/DrvHostValidationKit.cpp
r70493 r73370 132 132 133 133 if (pCfgAcq) 134 pCfgAcq->c FrameBufferHint= _1K;134 pCfgAcq->cfPeriod = _1K; 135 135 136 136 return VINF_SUCCESS; … … 194 194 { 195 195 if (pCfgAcq) 196 pCfgAcq->c FrameBufferHint= PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDbg->Out.cbPlayBuffer);196 pCfgAcq->cfPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDbg->Out.cbPlayBuffer); 197 197 } 198 198 -
trunk/src/VBox/Devices/Audio/HDAStream.cpp
r73241 r73370 197 197 /* Set scheduling hint (if available). */ 198 198 if (pThis->u16TimerHz) 199 pCfg->Device.uSchedulingHintMs = 1000 /* ms */ / pThis->u16TimerHz;199 pCfg->Device.uSchedulingHintMs = 1000 /* ms */ / pThis->u16TimerHz; 200 200 201 201 /* (Re-)Allocate the stream's internal DMA buffer, based on the PCM properties we just got above. */ -
trunk/src/VBox/Devices/Audio/HDAStream.h
r73244 r73370 165 165 * Should match SDFMT. */ 166 166 PDMAUDIOSTREAMCFG Cfg; 167 uint32_t Padding3; 167 168 #ifdef HDA_USE_DMA_ACCESS_HANDLER 168 169 /** List of DMA handlers. */ … … 175 176 uint16_t cbDMALeft; 176 177 /** Unused, padding. */ 177 uint8_t abPadding 3[2+4];178 uint8_t abPadding4[2+4]; 178 179 } HDASTREAMSTATE; 179 180 AssertCompileSizeAlignment(HDASTREAMSTATE, 8); -
trunk/src/VBox/Main/include/DrvAudioVRDE.h
r70644 r73370 47 47 public: 48 48 49 void onVRDEClientConnect(uint32_t uClientID); 50 void onVRDEClientDisconnect(uint32_t uClientID); 49 51 int onVRDEControl(bool fEnable, uint32_t uFlags); 50 52 int onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin); -
trunk/src/VBox/Main/src-client/ConsoleVRDPServer.cpp
r73097 r73370 933 933 DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackClientConnect(void *pvCallback, uint32_t u32ClientId) 934 934 { 935 ConsoleVRDPServer * server = static_cast<ConsoleVRDPServer*>(pvCallback);936 937 server->mConsole->i_VRDPClientConnect(u32ClientId);935 ConsoleVRDPServer *pServer = static_cast<ConsoleVRDPServer*>(pvCallback); 936 937 pServer->mConsole->i_VRDPClientConnect(u32ClientId); 938 938 939 939 /* Should the server report usage of an interface for each client? 940 940 * Similar to Intercept. 941 941 */ 942 int c = ASMAtomicIncS32(& server->mcClients);942 int c = ASMAtomicIncS32(&pServer->mcClients); 943 943 if (c == 1) 944 944 { 945 945 /* Features which should be enabled only if there is a client. */ 946 server->remote3DRedirect(true); 947 } 946 pServer->remote3DRedirect(true); 947 } 948 949 #ifdef VBOX_WITH_AUDIO_VRDE 950 AudioVRDE *pVRDE = pServer->mConsole->i_getAudioVRDE(); 951 if (pVRDE) 952 pVRDE->onVRDEClientConnect(u32ClientId); 953 #endif 948 954 } 949 955 … … 964 970 AudioVRDE *pVRDE = pServer->mConsole->i_getAudioVRDE(); 965 971 if (pVRDE) 972 { 966 973 pVRDE->onVRDEInputIntercept(false /* fIntercept */); 974 pVRDE->onVRDEClientDisconnect(u32ClientId); 975 } 967 976 #endif 968 977 } -
trunk/src/VBox/Main/src-client/DrvAudioVRDE.cpp
r70644 r73370 5 5 6 6 /* 7 * Copyright (C) 2013-201 7Oracle Corporation7 * Copyright (C) 2013-2018 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 59 59 /** Pointer to the DrvAudio port interface that is above us. */ 60 60 PPDMIAUDIOCONNECTOR pDrvAudio; 61 /** Number of connected clients to this VRDE instance. */ 62 uint32_t cClients; 61 63 } DRVAUDIOVRDE, *PDRVAUDIOVRDE; 62 64 … … 69 71 struct 70 72 { 71 /** Number of audio frames this stream can handle at once. */72 uint32_t cfMax;73 73 /** Circular buffer for holding the recorded audio frames from the host. */ 74 74 PRTCIRCBUF pCircBuf; … … 76 76 struct 77 77 { 78 /** Timestamp (in virtual time ticks) of the last audio playback (of the VRDP server). */ 79 uint64_t ticksPlayedLast; 80 /** Internal counter (in audio frames) to track if and how much we can write to the VRDP server 81 * for the current time period. */ 82 uint64_t cfToWrite; 78 uint32_t last; 83 79 } Out; 84 80 }; … … 90 86 static int vrdeCreateStreamIn(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 91 87 { 92 pStreamVRDE->In.cfMax = _1K; /** @todo Make this configurable. */ 93 94 int rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, pStreamVRDE->In.cfMax * (pCfgReq->Props.cBits / 8) /* Bytes */); 88 RT_NOREF(pCfgReq); 89 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 90 91 pCfgAcq->Props.uHz = 22050; /* The VRDP server's internal frequency. */ 92 pCfgAcq->Props.cChannels = 2; 93 pCfgAcq->Props.cBits = 16; 94 pCfgAcq->Props.fSigned = true; 95 96 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */ 97 const uint32_t cfVRDPServer = DrvAudioHlpMsToFrames(&pCfgAcq->Props, 200 /* ms */); 98 99 int rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, DrvAudioHlpFramesToBytes(&pCfgAcq->Props, cfVRDPServer)); 95 100 if (RT_SUCCESS(rc)) 96 {97 if (pCfgAcq)98 {99 /*100 * Because of historical reasons the VRDP server operates on st_sample_t structures internally,101 * which is 2 * int64_t for left/right (stereo) channels.102 *103 * As the audio connector also uses this format, set the layout to "raw" and just let pass through104 * the data without any layout modification needed.105 */106 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;107 pCfgAcq->cFrameBufferHint = pStreamVRDE->In.cfMax;108 }109 }110 111 return rc;112 }113 114 115 static int vrdeCreateStreamOut(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)116 {117 RT_NOREF(pStreamVRDE, pCfgReq);118 119 if (pCfgAcq)120 101 { 121 102 /* … … 126 107 * the data without any layout modification needed. 127 108 */ 128 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW; 129 pCfgAcq->cFrameBufferHint = _4K; /** @todo Make this configurable. */ 109 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW; 110 111 pCfgAcq->Backend.cfPeriod = cfVRDPServer; 112 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */ 113 pCfgAcq->Backend.cfPreBuf = pCfgAcq->Backend.cfPeriod; 114 } 115 116 return rc; 117 } 118 119 120 static int vrdeCreateStreamOut(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 121 { 122 RT_NOREF(pStreamVRDE, pCfgReq); 123 124 if (pCfgAcq) 125 { 126 /* 127 * Because of historical reasons the VRDP server operates on st_sample_t structures internally, 128 * which is 2 * int64_t for left/right (stereo) channels. 129 * 130 * As the audio connector also uses this format, set the layout to "raw" and just let pass through 131 * the data without any layout modification needed. 132 */ 133 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW; 134 135 pCfgAcq->Props.uHz = 22050; /* The VRDP server's internal frequency. */ 136 pCfgAcq->Props.cChannels = 2; 137 pCfgAcq->Props.cBits = 16; 138 pCfgAcq->Props.fSigned = true; 139 pCfgAcq->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cBits, pCfgAcq->Props.cChannels); 140 141 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */ 142 pCfgAcq->Backend.cfPeriod = DrvAudioHlpMsToFrames(&pCfgAcq->Props, 10 /* ms */); 143 pCfgAcq->Backend.cfBufferSize = DrvAudioHlpMsToFrames(&pCfgAcq->Props, 200 /* ms */); 144 pCfgAcq->Backend.cfPreBuf = pCfgAcq->Backend.cfBufferSize; 130 145 } 131 146 … … 158 173 case PDMAUDIOSTREAMCMD_ENABLE: 159 174 { 160 rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE, pStreamVRDE->In.cfMax, 175 rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE, 176 DrvAudioHlpMsToFrames(&pStreamVRDE->pCfg->Props, 200 /* ms */), 161 177 pStreamVRDE->pCfg->Props.uHz, pStreamVRDE->pCfg->Props.cChannels, 162 178 pStreamVRDE->pCfg->Props.cBits); … … 280 296 pProps->fSigned); 281 297 298 const uint64_t ticksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns); 299 const uint64_t ticksElapsed = ticksNow - pStreamVRDE->Out.last; 300 const uint64_t ticksPerSec = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns); 301 302 /* Remember when frames were consumed. */ 303 pStreamVRDE->Out.last = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns); 304 305 Log3Func(("FOOO -- %d\n", (int)((2 * ticksElapsed * pProps->uHz + ticksPerSec) / ticksPerSec / 2))); 306 282 307 /* Use the internal counter to track if we (still) can write to the VRDP server 283 308 * or if we need to wait another round (time slot). */ 284 uint32_t cfToWrite = pStreamVRDE->Out.cfToWrite;309 uint32_t cfToWrite = cfLive; //pStreamVRDE->Out.cfToWrite; 285 310 286 311 Log3Func(("cfLive=%RU32, cfToWrite=%RU32\n", cfLive, cfToWrite)); … … 319 344 if (RT_SUCCESS(rc)) 320 345 { 321 /* Subtract written frames from the counter. */322 Assert(pStreamVRDE->Out.cfToWrite >= cfWritten);323 pStreamVRDE->Out.cfToWrite -= cfWritten;324 325 /* Remember when frames were consumed. */326 pStreamVRDE->Out.ticksPlayedLast = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);327 328 346 /* Return frames instead of bytes here 329 347 * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */ … … 394 412 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVRDEGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir) 395 413 { 414 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio); 415 AssertPtrReturn(pDrv, PDMAUDIOBACKENDSTS_ERROR); 416 396 417 RT_NOREF(enmDir); 397 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);398 418 399 419 return PDMAUDIOBACKENDSTS_RUNNING; … … 516 536 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream; 517 537 518 const uint64_t ticksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);519 const uint64_t ticksElapsed = ticksNow - pStreamVRDE->Out.ticksPlayedLast;520 const uint64_t ticksPerSec = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);521 522 538 PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->pCfg->Props; 523 539 524 /* Minimize the rounding error: frames = int((ticks * freq) / ticks_per_second + 0.5). */ 525 pStreamVRDE->Out.cfToWrite = (int)((2 * ticksElapsed * pProps->uHz + ticksPerSec) / ticksPerSec / 2); 540 RT_NOREF(pDrv, pProps); 526 541 527 542 /* Return frames instead of bytes here 528 543 * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */ 529 return pStreamVRDE->Out.cfToWrite;544 return _16K; // pStreamVRDE->Out.cfToWrite; 530 545 } 531 546 … … 536 551 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioVRDEStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 537 552 { 538 RT_NOREF(pInterface, pStream); 539 540 return (PDMAUDIOSTREAMSTS_FLAG_INITIALIZED | PDMAUDIOSTREAMSTS_FLAG_ENABLED); 553 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio); 554 RT_NOREF(pStream); 555 556 PDMAUDIOSTREAMSTS streamSts = PDMAUDIOSTREAMSTS_FLAG_INITIALIZED; 557 558 if (pDrv->cClients) /* If any clients are connected, flag the stream as enabled. */ 559 streamSts |= PDMAUDIOSTREAMSTS_FLAG_ENABLED; 560 561 return streamSts; 541 562 } 542 563 … … 595 616 596 617 return VINF_SUCCESS; 618 } 619 620 621 void AudioVRDE::onVRDEClientConnect(uint32_t uClientID) 622 { 623 RT_NOREF(uClientID); 624 625 LogRel2(("Audio: VRDE client connected\n")); 626 mpDrv->cClients++; 627 } 628 629 630 void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID) 631 { 632 RT_NOREF(uClientID); 633 634 LogRel2(("Audio: VRDE client disconnected\n")); 635 Assert(mpDrv->cClients); 636 mpDrv->cClients--; 597 637 } 598 638 … … 719 759 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */ 720 760 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser; 761 pThis->cClients = 0; 721 762 722 763 /* -
trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
r73104 r73370 498 498 AssertFailed(); 499 499 500 if (pCfgAcq)501 pCfgAcq->cFrameBufferHint = 0;502 503 500 LogRel2(("VideoRec: Support for surround audio not implemented yet\n")); 504 501 return VERR_NOT_SUPPORTED; … … 525 522 pCfgAcq->Props.uHz = pSink->Codec.Parms.uHz; 526 523 pCfgAcq->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cBits, pCfgAcq->Props.cChannels); 527 pCfgAcq->cFrameBufferHint = _4K; /** @todo Make this configurable. */ 524 525 /* Every Opus frame marks a period for now. Optimize this later. */ 526 pCfgAcq->Backend.cfPeriod = DrvAudioHlpMsToFrames(&pCfgAcq->Props, pSink->Codec.Opus.msFrame); /** @todo Make this configurable. */ 528 527 } 529 528 }
Note:
See TracChangeset
for help on using the changeset viewer.