Changeset 88433 in vbox for trunk/src/VBox/Devices
- Timestamp:
- Apr 9, 2021 12:55:19 PM (4 years ago)
- Location:
- trunk/src/VBox/Devices/Audio
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/AudioMixBuffer.cpp
r88356 r88433 400 400 * Note: Currently does not handle any endianness conversion yet! 401 401 */ 402 #define AUDMIXBUF_CONVERT( _aName, _aType, _aMin, _aMax, _aSigned, _aShift) \402 #define AUDMIXBUF_CONVERT(a_Name, a_Type, _aMin, _aMax, _aSigned, _aShift) \ 403 403 /* Clips a specific output value to a single sample value. */ \ 404 DECLINLINE(int64_t) audioMixBufClipFrom## _aName(_aType aVal) \404 DECLINLINE(int64_t) audioMixBufClipFrom##a_Name(a_Type aVal) \ 405 405 { \ 406 406 /* left shifting of signed values is not defined, therefore the intermediate uint64_t cast */ \ … … 411 411 \ 412 412 /* Clips a single sample value to a specific output value. */ \ 413 DECLINLINE( _aType) audioMixBufClipTo##_aName(int64_t iVal) \413 DECLINLINE(a_Type) audioMixBufClipTo##a_Name(int64_t iVal) \ 414 414 { \ 415 415 /*if (iVal >= 0x7fffffff) return _aMax; if (iVal < -INT64_C(0x80000000)) return _aMin;*/ \ … … 417 417 { \ 418 418 if (_aSigned) \ 419 return ( _aType) (iVal >> (32 - _aShift)); \420 return ( _aType) ((iVal >> (32 - _aShift)) + ((_aMax >> 1) + 1)); \419 return (a_Type) (iVal >> (32 - _aShift)); \ 420 return (a_Type) ((iVal >> (32 - _aShift)) + ((_aMax >> 1) + 1)); \ 421 421 } \ 422 422 return iVal >= 0 ? _aMax : _aMin; \ 423 423 } \ 424 424 \ 425 DECLCALLBACK(uint32_t) audioMixBufConvFrom## _aName##Stereo(PPDMAUDIOFRAME paDst, const void *pvSrc, uint32_t cbSrc, \425 DECLCALLBACK(uint32_t) audioMixBufConvFrom##a_Name##Stereo(PPDMAUDIOFRAME paDst, const void *pvSrc, uint32_t cbSrc, \ 426 426 PCAUDMIXBUFCONVOPTS pOpts) \ 427 427 { \ 428 _aType const *pSrc = (_aType const *)pvSrc; \429 uint32_t cFrames = RT_MIN(pOpts->cFrames, cbSrc / sizeof( _aType)); \428 a_Type const *pSrc = (a_Type const *)pvSrc; \ 429 uint32_t cFrames = RT_MIN(pOpts->cFrames, cbSrc / sizeof(a_Type)); \ 430 430 AUDMIXBUF_MACRO_LOG(("cFrames=%RU32, BpS=%zu, lVol=%RU32, rVol=%RU32\n", \ 431 pOpts->cFrames, sizeof( _aType), pOpts->From.Volume.uLeft, pOpts->From.Volume.uRight)); \431 pOpts->cFrames, sizeof(a_Type), pOpts->From.Volume.uLeft, pOpts->From.Volume.uRight)); \ 432 432 for (uint32_t i = 0; i < cFrames; i++) \ 433 433 { \ 434 paDst->i64LSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom## _aName(*pSrc++), pOpts->From.Volume.uLeft ) >> AUDIOMIXBUF_VOL_SHIFT; \435 paDst->i64RSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom## _aName(*pSrc++), pOpts->From.Volume.uRight) >> AUDIOMIXBUF_VOL_SHIFT; \434 paDst->i64LSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom##a_Name(*pSrc++), pOpts->From.Volume.uLeft ) >> AUDIOMIXBUF_VOL_SHIFT; \ 435 paDst->i64RSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom##a_Name(*pSrc++), pOpts->From.Volume.uRight) >> AUDIOMIXBUF_VOL_SHIFT; \ 436 436 paDst++; \ 437 437 } \ … … 440 440 } \ 441 441 \ 442 DECLCALLBACK(uint32_t) audioMixBufConvFrom## _aName##Mono(PPDMAUDIOFRAME paDst, const void *pvSrc, uint32_t cbSrc, \442 DECLCALLBACK(uint32_t) audioMixBufConvFrom##a_Name##Mono(PPDMAUDIOFRAME paDst, const void *pvSrc, uint32_t cbSrc, \ 443 443 PCAUDMIXBUFCONVOPTS pOpts) \ 444 444 { \ 445 _aType const *pSrc = (_aType const *)pvSrc; \446 const uint32_t cFrames = RT_MIN(pOpts->cFrames, cbSrc / sizeof( _aType)); \445 a_Type const *pSrc = (a_Type const *)pvSrc; \ 446 const uint32_t cFrames = RT_MIN(pOpts->cFrames, cbSrc / sizeof(a_Type)); \ 447 447 AUDMIXBUF_MACRO_LOG(("cFrames=%RU32, BpS=%zu, lVol=%RU32, rVol=%RU32\n", \ 448 cFrames, sizeof( _aType), pOpts->From.Volume.uLeft, pOpts->From.Volume.uRight)); \448 cFrames, sizeof(a_Type), pOpts->From.Volume.uLeft, pOpts->From.Volume.uRight)); \ 449 449 for (uint32_t i = 0; i < cFrames; i++) \ 450 450 { \ 451 paDst->i64LSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom## _aName(*pSrc), pOpts->From.Volume.uLeft) >> AUDIOMIXBUF_VOL_SHIFT; \452 paDst->i64RSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom## _aName(*pSrc), pOpts->From.Volume.uRight) >> AUDIOMIXBUF_VOL_SHIFT; \451 paDst->i64LSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom##a_Name(*pSrc), pOpts->From.Volume.uLeft) >> AUDIOMIXBUF_VOL_SHIFT; \ 452 paDst->i64RSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom##a_Name(*pSrc), pOpts->From.Volume.uRight) >> AUDIOMIXBUF_VOL_SHIFT; \ 453 453 pSrc++; \ 454 454 paDst++; \ … … 458 458 } \ 459 459 \ 460 DECLCALLBACK(void) audioMixBufConvTo## _aName##Stereo(void *pvDst, PCPDMAUDIOFRAME paSrc, PCAUDMIXBUFCONVOPTS pOpts) \460 DECLCALLBACK(void) audioMixBufConvTo##a_Name##Stereo(void *pvDst, PCPDMAUDIOFRAME paSrc, PCAUDMIXBUFCONVOPTS pOpts) \ 461 461 { \ 462 462 PCPDMAUDIOFRAME pSrc = paSrc; \ 463 _aType *pDst = (_aType *)pvDst; \463 a_Type *pDst = (a_Type *)pvDst; \ 464 464 uint32_t cFrames = pOpts->cFrames; \ 465 465 while (cFrames--) \ 466 466 { \ 467 467 AUDMIXBUF_MACRO_LOG(("%p: l=%RI64, r=%RI64\n", pSrc, pSrc->i64LSample, pSrc->i64RSample)); \ 468 pDst[0] = audioMixBufClipTo## _aName(pSrc->i64LSample); \469 pDst[1] = audioMixBufClipTo## _aName(pSrc->i64RSample); \468 pDst[0] = audioMixBufClipTo##a_Name(pSrc->i64LSample); \ 469 pDst[1] = audioMixBufClipTo##a_Name(pSrc->i64RSample); \ 470 470 AUDMIXBUF_MACRO_LOG(("\t-> l=%RI16, r=%RI16\n", pDst[0], pDst[1])); \ 471 471 pDst += 2; \ … … 474 474 } \ 475 475 \ 476 DECLCALLBACK(void) audioMixBufConvTo## _aName##Mono(void *pvDst, PCPDMAUDIOFRAME paSrc, PCAUDMIXBUFCONVOPTS pOpts) \476 DECLCALLBACK(void) audioMixBufConvTo##a_Name##Mono(void *pvDst, PCPDMAUDIOFRAME paSrc, PCAUDMIXBUFCONVOPTS pOpts) \ 477 477 { \ 478 478 PCPDMAUDIOFRAME pSrc = paSrc; \ 479 _aType *pDst = (_aType *)pvDst; \479 a_Type *pDst = (a_Type *)pvDst; \ 480 480 uint32_t cFrames = pOpts->cFrames; \ 481 481 while (cFrames--) \ 482 482 { \ 483 *pDst++ = audioMixBufClipTo## _aName((pSrc->i64LSample + pSrc->i64RSample) / 2); \483 *pDst++ = audioMixBufClipTo##a_Name((pSrc->i64LSample + pSrc->i64RSample) / 2); \ 484 484 pSrc++; \ 485 485 } \ 486 } 486 } \ 487 /* Encoders for peek: */ \ 488 \ 489 /* 2ch -> 2ch */ \ 490 static DECLCALLBACK(void) RT_CONCAT(audioMixBufEncode2ChTo2Ch,a_Name)(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, \ 491 PAUDIOMIXBUFPEEKSTATE pState) \ 492 { \ 493 RT_NOREF_PV(pState); \ 494 a_Type *pDst = (a_Type *)pvDst; \ 495 while (cFrames-- > 0) \ 496 { \ 497 pDst[0] = audioMixBufClipTo##a_Name(pi64Src[0]); \ 498 pDst[1] = audioMixBufClipTo##a_Name(pi64Src[1]); \ 499 AUDMIXBUF_MACRO_LOG(("%p: %RI64 / %RI64 => %RI64 / %RI64\n", \ 500 pi64Src[0], pi64Src[0], pi64Src[1], (int64_t)pDst[0], (int64_t)pDst[1])); \ 501 pDst += 2; \ 502 pi64Src += 2; \ 503 } \ 504 } \ 505 \ 506 /* 2ch -> 1ch */ \ 507 static DECLCALLBACK(void) RT_CONCAT(audioMixBufEncode2ChTo1Ch,a_Name)(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, \ 508 PAUDIOMIXBUFPEEKSTATE pState) \ 509 { \ 510 RT_NOREF_PV(pState); \ 511 a_Type *pDst = (a_Type *)pvDst; \ 512 while (cFrames-- > 0) \ 513 { \ 514 pDst[0] = audioMixBufClipTo##a_Name((pi64Src[0] + pi64Src[1]) / 2); \ 515 pDst += 1; \ 516 pi64Src += 2; \ 517 } \ 518 } \ 519 \ 520 /* 1ch -> 2ch */ \ 521 static DECLCALLBACK(void) RT_CONCAT(audioMixBufEncode1ChTo2Ch,a_Name)(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, \ 522 PAUDIOMIXBUFPEEKSTATE pState) \ 523 { \ 524 RT_NOREF_PV(pState); \ 525 a_Type *pDst = (a_Type *)pvDst; \ 526 while (cFrames-- > 0) \ 527 { \ 528 pDst[0] = pDst[1] = audioMixBufClipTo##a_Name(pi64Src[0]); \ 529 pDst += 2; \ 530 pi64Src += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */ \ 531 } \ 532 } \ 533 /* 1ch -> 1ch */ \ 534 static DECLCALLBACK(void) RT_CONCAT(audioMixBufEncode1ChTo1Ch,a_Name)(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, \ 535 PAUDIOMIXBUFPEEKSTATE pState) \ 536 { \ 537 RT_NOREF_PV(pState); \ 538 a_Type *pDst = (a_Type *)pvDst; \ 539 while (cFrames-- > 0) \ 540 { \ 541 pDst[0] = audioMixBufClipTo##a_Name(pi64Src[0]); \ 542 pDst += 1; \ 543 pi64Src += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */ \ 544 } \ 545 } 546 487 547 488 548 /* audioMixBufConvXXXS8: 8 bit, signed. */ … … 529 589 } 530 590 591 592 static DECLCALLBACK(void) 593 audioMixBufEncode2ChTo2ChRaw(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, PAUDIOMIXBUFPEEKSTATE pState) 594 { 595 RT_NOREF_PV(pState); 596 memcpy(pvDst, pi64Src, sizeof(int64_t) * 2 * cFrames); 597 } 598 599 static DECLCALLBACK(void) 600 audioMixBufEncode2ChTo1ChRaw(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, PAUDIOMIXBUFPEEKSTATE pState) 601 { 602 RT_NOREF_PV(pState); 603 int64_t *pi64Dst = (int64_t *)pvDst; 604 while (cFrames-- > 0) 605 { 606 *pi64Dst = (pi64Src[0] + pi64Src[1]) / 2; 607 pi64Dst += 1; 608 pi64Src += 2; 609 } 610 } 611 612 static DECLCALLBACK(void) 613 audioMixBufEncode1ChTo2ChRaw(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, PAUDIOMIXBUFPEEKSTATE pState) 614 { 615 RT_NOREF_PV(pState); 616 int64_t *pi64Dst = (int64_t *)pvDst; 617 while (cFrames-- > 0) 618 { 619 pi64Dst[0] = pi64Dst[1] = *pi64Src; 620 pi64Dst += 2; 621 pi64Src += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */ 622 } 623 } 624 625 static DECLCALLBACK(void) 626 audioMixBufEncode1ChTo1ChRaw(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, PAUDIOMIXBUFPEEKSTATE pState) 627 { 628 RT_NOREF_PV(pState); 629 /** @todo memcpy(pvDst, pi64Src, sizeof(int64_t) * 1 * cFrames); when we do 630 * multi channel mixbuf support. */ 631 int64_t *pi64Dst = (int64_t *)pvDst; 632 while (cFrames-- > 0) 633 { 634 *pi64Dst = *pi64Src; 635 pi64Dst += 1; 636 pi64Src += 2; 637 } 638 } 531 639 532 640 … … 564 672 PPDMAUDIOFRAME pDst = paDst; \ 565 673 PPDMAUDIOFRAME pDstEnd = &paDst[cDstFrames]; \ 566 PDMAUDIOFRAME frameLast = pRate->Src FrameLast; \674 PDMAUDIOFRAME frameLast = pRate->SrcLast.Frame; \ 567 675 \ 568 676 while ((uintptr_t)pDst < (uintptr_t)pDstEnd) \ … … 608 716 } \ 609 717 \ 610 pRate->Src FrameLast= frameLast; \718 pRate->SrcLast.Frame = frameLast; \ 611 719 if (pcDstWritten) \ 612 720 *pcDstWritten = pDst - paDst; \ … … 1253 1361 1254 1362 #ifdef DEBUG 1363 1255 1364 /** 1256 1365 * Prints a single mixing buffer. … … 1392 1501 audioMixBufDbgPrintInternal(pMixBuf, __FUNCTION__); 1393 1502 } 1503 1394 1504 #endif /* DEBUG */ 1395 1505 … … 1654 1764 } 1655 1765 1766 1767 /** 1768 * Drops all the frames in the given mixing buffer. 1769 * 1770 * Similar to AudioMixBufReset, only it doesn't bother clearing the buffer. 1771 * 1772 * @param pMixBuf The mixing buffer. 1773 */ 1774 void AudioMixBufDrop(PAUDIOMIXBUF pMixBuf) 1775 { 1776 AssertPtrReturnVoid(pMixBuf); 1777 AssertReturnVoid(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC); 1778 1779 AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName)); 1780 1781 pMixBuf->offRead = 0; 1782 pMixBuf->offWrite = 0; 1783 pMixBuf->cMixed = 0; 1784 pMixBuf->cUsed = 0; 1785 } 1786 1787 1788 /* 1789 * Resampling core. 1790 */ 1791 /** @todo Separate down- and up-sampling, borrow filter code from RDP. */ 1792 #define COPY_LAST_FRAME_1CH(a_pi64Dst, a_pi64Src, a_cChannels) do { \ 1793 (a_pi64Dst)[0] = (a_pi64Src)[0]; \ 1794 } while (0) 1795 #define COPY_LAST_FRAME_2CH(a_pi64Dst, a_pi64Src, a_cChannels) do { \ 1796 (a_pi64Dst)[0] = (a_pi64Src)[0]; \ 1797 (a_pi64Dst)[1] = (a_pi64Src)[1]; \ 1798 } while (0) 1799 1800 #define INTERPOLATE_ONE(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, a_iCh) \ 1801 (a_pi64Dst)[a_iCh] = ((a_pi64Last)[a_iCh] * a_i64FactorLast + (a_pi64Src)[a_iCh] * a_i64FactorCur) >> 32 1802 #define INTERPOLATE_1CH(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, a_cChannels) do { \ 1803 INTERPOLATE_ONE(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, 0); \ 1804 } while (0) 1805 #define INTERPOLATE_2CH(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, a_cChannels) do { \ 1806 INTERPOLATE_ONE(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, 0); \ 1807 INTERPOLATE_ONE(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, 1); \ 1808 } while (0) 1809 1810 #define AUDIOMIXBUF_RESAMPLE(a_cChannels, a_Suffix) \ 1811 /** @returns Number of destination frames written. */ \ 1812 static uint32_t \ 1813 audioMixBufResample##a_cChannels##Ch##a_Suffix(int64_t *pi64Dst, uint32_t cDstFrames, \ 1814 int64_t const *pi64Src, uint32_t cSrcFrames, uint32_t *pcSrcFramesRead, \ 1815 PAUDIOSTREAMRATE pRate) \ 1816 { \ 1817 Log5(("Src: %RU32 L %RU32; Dst: %RU32 L%RU32; uDstInc=%#RX64\n", \ 1818 pRate->offSrc, cSrcFrames, RT_HI_U32(pRate->offDst), cDstFrames, pRate->uDstInc)); \ 1819 int64_t * const pi64DstStart = pi64Dst; \ 1820 int64_t const * const pi64SrcStart = pi64Src; \ 1821 \ 1822 int64_t ai64LastFrame[a_cChannels]; \ 1823 COPY_LAST_FRAME_##a_cChannels##CH(ai64LastFrame, pRate->SrcLast.ai64Samples, a_cChannels); \ 1824 \ 1825 while (cDstFrames > 0 && cSrcFrames > 0) \ 1826 { \ 1827 int32_t const cSrcNeeded = RT_HI_U32(pRate->offDst) - pRate->offSrc + 1; \ 1828 if (cSrcNeeded > 0) \ 1829 { \ 1830 if ((uint32_t)cSrcNeeded + 1 < cSrcFrames) \ 1831 { \ 1832 pRate->offSrc += (uint32_t)cSrcNeeded; \ 1833 cSrcFrames -= (uint32_t)cSrcNeeded; \ 1834 pi64Src += (uint32_t)cSrcNeeded * a_cChannels; \ 1835 COPY_LAST_FRAME_##a_cChannels##CH(ai64LastFrame, &pi64Src[-a_cChannels], a_cChannels); \ 1836 } \ 1837 else \ 1838 { \ 1839 pi64Src += cSrcFrames * a_cChannels; \ 1840 pRate->offSrc += cSrcFrames; \ 1841 COPY_LAST_FRAME_##a_cChannels##CH(pRate->SrcLast.ai64Samples, &pi64Src[-a_cChannels], a_cChannels); \ 1842 *pcSrcFramesRead = (pi64Src - pi64SrcStart) / a_cChannels; \ 1843 return (pi64Dst - pi64DstStart) / a_cChannels; \ 1844 } \ 1845 } \ 1846 \ 1847 /* Interpolate. */ \ 1848 int64_t const offFactorCur = pRate->offDst & UINT32_MAX; \ 1849 int64_t const offFactorLast = (int64_t)_4G - offFactorCur; \ 1850 INTERPOLATE_##a_cChannels##CH(pi64Dst, pi64Src, ai64LastFrame, offFactorCur, offFactorLast, a_cChannels); \ 1851 \ 1852 /* Advance. */ \ 1853 pRate->offDst += pRate->uDstInc; \ 1854 pi64Dst += a_cChannels; \ 1855 cDstFrames -= 1; \ 1856 } \ 1857 \ 1858 COPY_LAST_FRAME_##a_cChannels##CH(pRate->SrcLast.ai64Samples, ai64LastFrame, a_cChannels); \ 1859 *pcSrcFramesRead = (pi64Src - pi64SrcStart) / a_cChannels; \ 1860 return (pi64Dst - pi64DstStart) / a_cChannels; \ 1861 } 1862 1863 AUDIOMIXBUF_RESAMPLE(1,Generic) 1864 AUDIOMIXBUF_RESAMPLE(2,Generic) 1865 1866 1867 /** 1868 * Initializes the peek state, setting up encoder and (if necessary) resampling. 1869 * 1870 * @returns VBox status code. 1871 */ 1872 int AudioMixBufInitPeekState(PCAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFPEEKSTATE pState, PCPDMAUDIOPCMPROPS pProps) 1873 { 1874 AssertPtr(pMixBuf); 1875 AssertPtr(pState); 1876 AssertPtr(pProps); 1877 1878 /* 1879 * Pick the encoding function first. 1880 */ 1881 uint8_t const cSrcCh = PDMAudioPropsChannels(&pMixBuf->Props); 1882 uint8_t const cDstCh = PDMAudioPropsChannels(pProps); 1883 pState->cSrcChannels = cSrcCh; 1884 pState->cDstChannels = cDstCh; 1885 pState->cbDstFrame = PDMAudioPropsFrameSize(pProps); 1886 if (PDMAudioPropsIsSigned(pProps)) 1887 { 1888 switch (cDstCh) 1889 { 1890 case 1: 1891 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE); 1892 switch (PDMAudioPropsSampleSize(pProps)) 1893 { 1894 case 1: 1895 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChS8 : audioMixBufEncode2ChTo1ChS8; 1896 break; 1897 case 2: 1898 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChS16 : audioMixBufEncode2ChTo1ChS16; 1899 break; 1900 case 4: 1901 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChS32 : audioMixBufEncode2ChTo1ChS32; 1902 break; 1903 case 8: 1904 AssertReturn(pProps->fRaw, VERR_DISK_INVALID_FORMAT); 1905 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChRaw : audioMixBufEncode2ChTo1ChRaw; 1906 break; 1907 default: 1908 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE); 1909 } 1910 break; 1911 case 2: 1912 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE); 1913 switch (PDMAudioPropsSampleSize(pProps)) 1914 { 1915 case 1: 1916 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChS8 : audioMixBufEncode2ChTo2ChS8; 1917 break; 1918 case 2: 1919 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChS16 : audioMixBufEncode2ChTo2ChS16; 1920 break; 1921 case 4: 1922 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChS32 : audioMixBufEncode2ChTo2ChS32; 1923 break; 1924 case 8: 1925 AssertReturn(pProps->fRaw, VERR_DISK_INVALID_FORMAT); 1926 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChRaw : audioMixBufEncode2ChTo2ChRaw; 1927 break; 1928 default: 1929 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE); 1930 } 1931 break; 1932 default: 1933 /* Note: We may have dedicated encoders for a few selected multichannel 1934 configurations, and generic ones that encodes channel by channel (i.e. 1935 add the mixer channel count, destination frame size, and an array of 1936 destination channel frame offsets to the state). */ 1937 AssertMsgFailedReturn(("from %u to %u channels is not implemented yet\n", cSrcCh, cDstCh), VERR_OUT_OF_RANGE); 1938 } 1939 } 1940 else 1941 { 1942 switch (cDstCh) 1943 { 1944 case 1: 1945 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE); 1946 switch (PDMAudioPropsSampleSize(pProps)) 1947 { 1948 case 1: 1949 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChU8 : audioMixBufEncode2ChTo1ChU8; 1950 break; 1951 case 2: 1952 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChU16 : audioMixBufEncode2ChTo1ChU16; 1953 break; 1954 case 4: 1955 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChU32 : audioMixBufEncode2ChTo1ChU32; 1956 break; 1957 default: 1958 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE); 1959 } 1960 break; 1961 case 2: 1962 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE); 1963 switch (PDMAudioPropsSampleSize(pProps)) 1964 { 1965 case 1: 1966 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChU8 : audioMixBufEncode2ChTo2ChU8; 1967 break; 1968 case 2: 1969 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChU16 : audioMixBufEncode2ChTo2ChU16; 1970 break; 1971 case 4: 1972 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChU32 : audioMixBufEncode2ChTo2ChU32; 1973 break; 1974 default: 1975 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE); 1976 } 1977 break; 1978 default: 1979 /* Note: We may have dedicated encoders for a few selected multichannel 1980 configurations, and generic ones that encodes channel by channel (i.e. 1981 add an array of destination channel frame offsets to the state). */ 1982 AssertMsgFailedReturn(("from %u to %u channels is not implemented yet\n", cSrcCh, cDstCh), VERR_OUT_OF_RANGE); 1983 } 1984 1985 } 1986 1987 /* 1988 * Do we need to set up frequency conversion? 1989 * 1990 * Some examples to get an idea of what uDstInc holds: 1991 * 44100 to 44100 -> (44100<<32) / 44100 = 0x01'00000000 (4294967296) 1992 * 22050 to 44100 -> (22050<<32) / 44100 = 0x00'80000000 (2147483648) 1993 * 44100 to 22050 -> (44100<<32) / 22050 = 0x02'00000000 (8589934592) 1994 * 44100 to 48000 -> (44100<<32) / 48000 = 0x00'EB333333 (3946001203.2) 1995 * 48000 to 44100 -> (48000<<32) / 44100 = 0x01'16A3B35F (4674794335.7823129251700680272109) 1996 */ 1997 uint32_t const uSrcHz = PDMAudioPropsHz(&pMixBuf->Props); 1998 uint32_t const uDstHz = PDMAudioPropsHz(pProps); 1999 RT_ZERO(pState->Rate); 2000 if (uSrcHz == uDstHz) 2001 { 2002 pState->Rate.fNoConversionNeeded = true; 2003 pState->Rate.uDstInc = RT_BIT_64(32); 2004 pState->Rate.pfnResample = NULL; 2005 } 2006 else 2007 { 2008 AssertReturn(uSrcHz != 0, VERR_INVALID_PARAMETER); 2009 pState->Rate.fNoConversionNeeded = false; 2010 pState->Rate.uDstInc = ((uint64_t)uSrcHz << 32) / uDstHz; 2011 switch (cSrcCh) 2012 { 2013 case 1: pState->Rate.pfnResample = audioMixBufResample1ChGeneric; break; 2014 case 2: pState->Rate.pfnResample = audioMixBufResample2ChGeneric; break; 2015 default: 2016 AssertMsgFailedReturn(("resampling %u changes is not implemented yet\n", cSrcCh), VERR_OUT_OF_RANGE); 2017 } 2018 } 2019 AUDMIXBUF_LOG(("%s: %RU32 Hz to %RU32 Hz => uDstInc=0x%'RX64\n", pMixBuf->pszName, uSrcHz, uDstHz, pState->Rate.uDstInc)); 2020 return VINF_SUCCESS; 2021 } 2022 2023 2024 /** 2025 * Worker for AudioMixBufPeek that handles the rate conversion case. 2026 */ 2027 DECL_NO_INLINE(static, void) 2028 AudioMixBufPeekResampling(PCAUDIOMIXBUF pMixBuf, uint32_t offSrcFrame, uint32_t cMaxSrcFrames, uint32_t *pcSrcFramesPeeked, 2029 PAUDIOMIXBUFPEEKSTATE pState, void *pvDst, uint32_t cbDst, uint32_t *pcbDstPeeked) 2030 { 2031 *pcSrcFramesPeeked = 0; 2032 *pcbDstPeeked = 0; 2033 while (cMaxSrcFrames > 0 && cbDst >= pState->cbDstFrame) 2034 { 2035 /* Rate conversion into temporary buffer. */ 2036 int64_t ai64DstRate[1024]; 2037 uint32_t cSrcFrames = RT_MIN(pMixBuf->cFrames - offSrcFrame, cMaxSrcFrames); 2038 uint32_t const cDstFrames = pState->Rate.pfnResample(ai64DstRate, RT_ELEMENTS(ai64DstRate) / pState->cDstChannels, 2039 &pMixBuf->pFrames[offSrcFrame].i64LSample, cSrcFrames, &cSrcFrames, 2040 &pState->Rate); 2041 *pcSrcFramesPeeked += cSrcFrames; 2042 cMaxSrcFrames -= cSrcFrames; 2043 offSrcFrame = (offSrcFrame + cSrcFrames) % pMixBuf->cFrames; 2044 2045 /* Encode the converted frames. */ 2046 uint32_t const cbDstEncoded = cDstFrames * pState->cbDstFrame; 2047 pState->pfnEncode(pvDst, ai64DstRate, cDstFrames, pState); 2048 *pcbDstPeeked += cbDstEncoded; 2049 cbDst -= cbDstEncoded; 2050 pvDst = (uint8_t *)pvDst + cbDstEncoded; 2051 } 2052 } 2053 2054 2055 2056 /** 2057 * Copies data out of the mixing buffer, converting it if needed, but leaves the 2058 * read offset untouched. 2059 * 2060 * @param pMixBuf The mixing buffer. 2061 * @param offSrcFrame The offset to start reading at relative to 2062 * current read position (offRead). The caller has 2063 * made sure there is at least this number of 2064 * frames available in the buffer before calling. 2065 * @param cMaxSrcFrames Maximum number of frames to read. 2066 * @param pcSrcFramesPeeked Where to return the actual number of frames read 2067 * from the mixing buffer. 2068 * @param pState Output configuration & conversion state. 2069 * @param pvDst The destination buffer. 2070 * @param cbDst The size of the destination buffer in bytes. 2071 * @param pcbDstPeeked Where to put the actual number of bytes 2072 * returned. 2073 */ 2074 void AudioMixBufPeek(PCAUDIOMIXBUF pMixBuf, uint32_t offSrcFrame, uint32_t cMaxSrcFrames, uint32_t *pcSrcFramesPeeked, 2075 PAUDIOMIXBUFPEEKSTATE pState, void *pvDst, uint32_t cbDst, uint32_t *pcbDstPeeked) 2076 { 2077 /* 2078 * Check inputs. 2079 */ 2080 AssertPtr(pMixBuf); 2081 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC); 2082 AssertPtr(pState); 2083 AssertPtr(pState->pfnEncode); 2084 Assert(pState->cSrcChannels == PDMAudioPropsChannels(&pMixBuf->Props)); 2085 Assert(cMaxSrcFrames > 0); 2086 Assert(cMaxSrcFrames <= pMixBuf->cFrames); 2087 Assert(offSrcFrame <= pMixBuf->cFrames); 2088 Assert(offSrcFrame + cMaxSrcFrames <= pMixBuf->cUsed); 2089 AssertPtr(pcSrcFramesPeeked); 2090 AssertPtr(pvDst); 2091 Assert(cbDst >= pState->cbDstFrame); 2092 AssertPtr(pcbDstPeeked); 2093 2094 /* 2095 * Make start frame absolute. 2096 */ 2097 offSrcFrame = (pMixBuf->offRead + offSrcFrame) % pMixBuf->cFrames; 2098 2099 /* 2100 * Hopefully no sample rate conversion is necessary... 2101 */ 2102 if (pState->Rate.fNoConversionNeeded) 2103 { 2104 /* Figure out how much we should convert. */ 2105 cMaxSrcFrames = RT_MIN(cMaxSrcFrames, cbDst / pState->cbDstFrame); 2106 *pcSrcFramesPeeked = cMaxSrcFrames; 2107 *pcbDstPeeked = cMaxSrcFrames * pState->cbDstFrame; 2108 2109 /* First chunk. */ 2110 uint32_t const cSrcFrames1 = RT_MIN(pMixBuf->cFrames - offSrcFrame, cMaxSrcFrames); 2111 pState->pfnEncode(pvDst, &pMixBuf->pFrames[offSrcFrame].i64LSample, cSrcFrames1, pState); 2112 2113 /* Another chunk from the start of the mixing buffer? */ 2114 if (cMaxSrcFrames > cSrcFrames1) 2115 pState->pfnEncode((uint8_t *)pvDst + cSrcFrames1 * pState->cbDstFrame, 2116 &pMixBuf->pFrames[0].i64LSample, cMaxSrcFrames - cSrcFrames1, pState); 2117 } 2118 else 2119 AudioMixBufPeekResampling(pMixBuf, offSrcFrame, cMaxSrcFrames, pcSrcFramesPeeked, pState, pvDst, cbDst, pcbDstPeeked); 2120 } 2121 2122 2123 /** 2124 * Advances the read position of the buffer. 2125 * 2126 * For use after done peeking with AudioMixBufPeek(). 2127 * 2128 * @param pMixBuf The mixing buffer. 2129 * @param cFrames Number of frames to advance. 2130 */ 2131 void AudioMixBufAdvance(PAUDIOMIXBUF pMixBuf, uint32_t cFrames) 2132 { 2133 AssertPtrReturnVoid(pMixBuf); 2134 AssertReturnVoid(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC); 2135 2136 AssertStmt(cFrames <= pMixBuf->cUsed, cFrames = pMixBuf->cUsed); 2137 pMixBuf->cUsed -= cFrames; 2138 pMixBuf->offRead = (pMixBuf->offRead + cFrames) % pMixBuf->cFrames; 2139 LogFlowFunc(("%s: Advanced %u frames: offRead=%u cUsed=%u\n", pMixBuf->pszName, cFrames, pMixBuf->offRead, pMixBuf->cUsed)); 2140 } 2141 2142 1656 2143 /** 1657 2144 * Sets the overall (master) volume. … … 1913 2400 const void *pvBuf, uint32_t cbBuf, uint32_t *pcWritten) 1914 2401 { 2402 /* 2403 * Assert sanity. 2404 */ 1915 2405 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER); 1916 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 2406 AssertReturn(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC, VERR_INVALID_HANDLE); 2407 Assert(pMixBuf->cFrames); 2408 AssertPtr(pMixBuf->pFrames); 2409 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 1917 2410 AssertPtrNullReturn(pcWritten, VERR_INVALID_POINTER); 1918 2411 1919 if (!cbBuf) 2412 /* 2413 * Make sure that we at least write a full audio frame. 2414 */ 2415 uint32_t const cFramesInBuf = PDMAudioPropsBytesToFrames(pSrcProps, cbBuf); 2416 if (cFramesInBuf > 0) 2417 { /* likely */ } 2418 else 1920 2419 { 1921 2420 if (pcWritten) 1922 2421 *pcWritten = 0; 2422 AssertReturn(cbBuf == 0, VERR_INVALID_PARAMETER); 1923 2423 return VINF_SUCCESS; 1924 2424 } 1925 2425 1926 /* Make sure that we at least write a full audio frame. */ 1927 AssertReturn(AUDIOMIXBUF_B2F(pMixBuf, cbBuf), VERR_INVALID_PARAMETER); 1928 1929 Assert(pMixBuf->cFrames); 1930 AssertPtr(pMixBuf->pFrames); 1931 2426 /* 2427 * Get the conversion function matching pSrcProps, doing special 2428 * optimizations for a muted buffer. 2429 */ 1932 2430 PFNAUDIOMIXBUFCONVFROM pfnConvFrom = NULL; 1933 2431 if (!pMixBuf->Volume.fMuted) … … 1942 2440 pfnConvFrom = &audioMixBufConvFromSilence; 1943 2441 1944 int rc = VINF_SUCCESS; 1945 1946 uint32_t cWritten = 0; 1947 1948 uint32_t cFree = pMixBuf->cFrames - pMixBuf->cUsed; 1949 if (cFree) 1950 { 1951 if ((pMixBuf->cFrames - pMixBuf->offWrite) == 0) 1952 pMixBuf->offWrite = 0; 1953 1954 uint32_t cToWrite = RT_MIN(AUDIOMIXBUF_B2F(pMixBuf, cbBuf), RT_MIN(pMixBuf->cFrames - pMixBuf->offWrite, cFree)); 1955 Assert(cToWrite); 1956 1957 AUDMIXBUFCONVOPTS convOpts; 1958 convOpts.From.Volume.fMuted = pMixBuf->Volume.fMuted; 1959 convOpts.From.Volume.uLeft = pMixBuf->Volume.uLeft; 1960 convOpts.From.Volume.uRight = pMixBuf->Volume.uRight; 1961 convOpts.cFrames = cToWrite; 1962 1963 cWritten = pfnConvFrom(pMixBuf->pFrames + pMixBuf->offWrite, 1964 pvBuf, AUDIOMIXBUF_F2B(pMixBuf, cToWrite), &convOpts); 1965 Assert(cWritten == cToWrite); 1966 2442 /* 2443 * Is there any free space left in the buffer? 2444 */ 2445 int rc = VINF_SUCCESS; 2446 uint32_t cFramesWritten = 0; 2447 uint32_t cFramesFree = pMixBuf->cFrames - pMixBuf->cUsed; 2448 if (cFramesFree > 0) 2449 { 2450 /* 2451 * There are potentially two writes we can make, one from offWrite and 2452 * a 2nd one from the start of the buffer. 2453 */ 2454 uint32_t cFramesToWrite1 = RT_MIN(pMixBuf->cFrames - pMixBuf->offWrite, cFramesFree); 2455 uint32_t cFramesToWrite2 = 0; 2456 if (cFramesToWrite1 >= cFramesInBuf) 2457 cFramesToWrite1 = cFramesInBuf; /* The first write can hold it all. */ 2458 else if (cFramesFree >= cFramesInBuf) 2459 cFramesToWrite2 = cFramesInBuf - cFramesToWrite1; /* If we wrap around, we can make it all fit. */ 2460 else if (cFramesToWrite1 < cFramesFree) 2461 cFramesToWrite2 = cFramesFree - cFramesToWrite1; /* Not enough free space. Must wrap around to fill it all, though. */ 2462 else 2463 cFramesToWrite1 = cFramesFree; /* Not enough free space, the read position is before the wraparound. */ 2464 Assert(cFramesToWrite1); 2465 Log4Func(("cbBuf=%#x cFramesInBuf=%#x cFramesFree=%#x offWrite=%#x cFrames=%#x => %#x + %#x\n", 2466 cbBuf, cFramesInBuf, cFramesFree, pMixBuf->offWrite, pMixBuf->cFrames, cFramesToWrite1, cFramesToWrite2)); 2467 2468 /* 2469 * Set up the conversion and do the first chunk. 2470 */ 2471 AUDMIXBUFCONVOPTS ConvOpts; 2472 ConvOpts.From.Volume.fMuted = pMixBuf->Volume.fMuted; 2473 ConvOpts.From.Volume.uLeft = pMixBuf->Volume.uLeft; 2474 ConvOpts.From.Volume.uRight = pMixBuf->Volume.uRight; 2475 ConvOpts.cFrames = cFramesToWrite1; 2476 2477 uint32_t const cbToWrite1 = PDMAudioPropsFramesToBytes(pSrcProps, cFramesToWrite1); 2478 cFramesWritten = pfnConvFrom(&pMixBuf->pFrames[pMixBuf->offWrite], 2479 pvBuf, PDMAudioPropsFramesToBytes(pSrcProps, cFramesToWrite1), &ConvOpts); 2480 Assert(cFramesWritten == cFramesToWrite1); 2481 2482 /* 2483 * Any 2nd buffer? 2484 */ 2485 if (cFramesToWrite2 > 0 && cFramesWritten == cFramesToWrite1) 2486 { 2487 ConvOpts.cFrames = cFramesToWrite2; 2488 uint32_t const cbToWrite2 = PDMAudioPropsFramesToBytes(pSrcProps, cFramesToWrite2); 2489 cFramesWritten += pfnConvFrom(&pMixBuf->pFrames[0], (uint8_t const *)pvBuf + cbToWrite1, cbToWrite2, &ConvOpts); 2490 Assert(cFramesWritten == cFramesToWrite2 + cFramesToWrite1); 2491 } 2492 2493 /* 2494 * Advance the buffer position. 2495 */ 2496 pMixBuf->cUsed += cFramesWritten; 2497 Assert(pMixBuf->cUsed <= pMixBuf->cFrames); 2498 2499 pMixBuf->offWrite = (pMixBuf->offWrite + cFramesWritten) % pMixBuf->cFrames; 2500 Assert(pMixBuf->offWrite < pMixBuf->cFrames); 2501 2502 /* 2503 * Debug stuff. 2504 */ 1967 2505 #ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA 1968 2506 RTFILE fh; 1969 2507 RTFileOpen(&fh, AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH "mixbuf_writecirc_ex.pcm", 1970 2508 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 1971 RTFileWrite(fh, pvBuf, AUDIOMIXBUF_F2B(pMixBuf, cToWrite), NULL);2509 RTFileWrite(fh, pvBuf, PDMAudioPropsFramesToBytes(pSrcProps, cFramesWritten), NULL); 1972 2510 RTFileClose(fh); 1973 2511 #endif 1974 pMixBuf->cUsed += cWritten;1975 Assert(pMixBuf->cUsed <= pMixBuf->cFrames);1976 1977 pMixBuf->offWrite = (pMixBuf->offWrite + cWritten) % pMixBuf->cFrames;1978 Assert(pMixBuf->offWrite <= pMixBuf->cFrames);1979 2512 } 1980 2513 else … … 1987 2520 1988 2521 if (pcWritten) 1989 *pcWritten = c Written;2522 *pcWritten = cFramesWritten; 1990 2523 1991 2524 #ifdef AUDMIXBUF_LOG_ENABLED 1992 2525 char szTmp[PDMAUDIOPROPSTOSTRING_MAX]; 1993 2526 #endif 1994 AUDMIXBUF_LOG(("%s: pSrcProps=%s, cbBuf=%RU32 (%RU32 frames), c Written=%RU32, rc=%Rrc\n", pMixBuf->pszName,1995 PDMAudioPropsToString(pSrcProps, szTmp, sizeof(szTmp)), cbBuf, AUDIOMIXBUF_B2F(pMixBuf, cbBuf), c Written, rc));2527 AUDMIXBUF_LOG(("%s: pSrcProps=%s, cbBuf=%RU32 (%RU32 frames), cFramesWritten=%RU32, rc=%Rrc\n", pMixBuf->pszName, 2528 PDMAudioPropsToString(pSrcProps, szTmp, sizeof(szTmp)), cbBuf, AUDIOMIXBUF_B2F(pMixBuf, cbBuf), cFramesWritten, rc)); 1996 2529 return rc; 1997 2530 } -
trunk/src/VBox/Devices/Audio/AudioMixBuffer.h
r88356 r88433 42 42 /** Current (absolute) offset in the input stream. */ 43 43 uint32_t offSrc; 44 /** Explicit alignment padding. */ 45 uint32_t u32AlignmentPadding; 44 /** Set if no conversion is necessary. */ 45 bool fNoConversionNeeded; 46 bool afPadding[3]; 47 46 48 /** Last processed frame of the input stream. 47 49 * Needed for interpolation. */ 48 PDMAUDIOFRAME SrcFrameLast; 50 union 51 { 52 int64_t ai64Samples[2]; 53 PDMAUDIOFRAME Frame; 54 } SrcLast; 55 56 /** 57 * Resampling function. 58 * @returns Number of destination frames written. 59 */ 60 DECLR3CALLBACKMEMBER(uint32_t, pfnResample, (int64_t *pi64Dst, uint32_t cDstFrames, 61 int64_t const *pi64Src, uint32_t cSrcFrames, uint32_t *pcSrcFramesRead, 62 struct AUDIOSTREAMRATE *pRate)); 63 49 64 } AUDIOSTREAMRATE; 50 65 /** Pointer to rate processing information of a stream. */ … … 129 144 /** Pointer to audio mixing buffer. */ 130 145 typedef struct AUDIOMIXBUF *PAUDIOMIXBUF; 146 /** Pointer to a const audio mixing buffer. */ 147 typedef struct AUDIOMIXBUF const *PCAUDIOMIXBUF; 148 149 150 /** 151 * State & config for AudioMixBufPeek created by AudioMixBufInitPeekState. 152 */ 153 typedef struct AUDIOMIXBUFPEEKSTATE 154 { 155 /** Encodes @a cFrames from @a paSrc to @a pvDst. */ 156 DECLR3CALLBACKMEMBER(void, pfnEncode,(void *pvDst, int64_t const *paSrc, uint32_t cFrames, struct AUDIOMIXBUFPEEKSTATE *pState)); 157 /** Sample rate conversion state (only used when needed). */ 158 AUDIOSTREAMRATE Rate; 159 /** Source (mixer) channels. */ 160 uint8_t cSrcChannels; 161 /** Destination (mixer) channels. */ 162 uint8_t cDstChannels; 163 /** Destination frame size. */ 164 uint8_t cbDstFrame; 165 } AUDIOMIXBUFPEEKSTATE; 166 /** Pointer to peek state & config. */ 167 typedef AUDIOMIXBUFPEEKSTATE *PAUDIOMIXBUFPEEKSTATE; 168 131 169 132 170 /** … … 234 272 int AudioMixBufInit(PAUDIOMIXBUF pMixBuf, const char *pszName, PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames); 235 273 void AudioMixBufDestroy(PAUDIOMIXBUF pMixBuf); 274 275 int AudioMixBufInitPeekState(PCAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFPEEKSTATE pState, PCPDMAUDIOPCMPROPS pDstProps); 276 void AudioMixBufPeek(PCAUDIOMIXBUF pMixBuf, uint32_t offSrcFrame, uint32_t cMaxSrcFrames, uint32_t *pcSrcFramesPeeked, 277 PAUDIOMIXBUFPEEKSTATE pState, void *pvDst, uint32_t cbDst, uint32_t *pcbDstPeeked); 278 void AudioMixBufAdvance(PAUDIOMIXBUF pMixBuf, uint32_t cFrames); 279 void AudioMixBufDrop(PAUDIOMIXBUF pMixBuf); 280 236 281 void AudioMixBufClear(PAUDIOMIXBUF pMixBuf); 237 282 void AudioMixBufFinish(PAUDIOMIXBUF pMixBuf, uint32_t cFramesToClear); -
trunk/src/VBox/Devices/Audio/AudioMixer.cpp
r88374 r88433 104 104 static void audioMixerSinkReset(PAUDMIXSINK pSink); 105 105 static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream); 106 static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink);107 static int audioMixerSinkMultiplexSync(PAUDMIXSINK pSink, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten);108 static int audioMixerSinkWriteToStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream);109 static int audioMixerSinkWriteToStreamEx(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream, uint32_t cbToWrite, uint32_t *pcbWritten);110 106 111 107 static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl); … … 575 571 /** @todo Validate fFlags. */ 576 572 /* ppStream is optional. */ 573 RT_NOREF(pDevIns); /* we'll probably be adding more statistics */ 577 574 578 575 /* … … 603 600 if (RT_SUCCESS(rc)) 604 601 { 605 rc = RTCircBufCreate(&pMixStream->pCircBuf, PDMAudioPropsMilliToBytes(&pSink->PCMProps, 100 /*ms*/)); /** @todo Make this configurable. */ 602 /* 603 * Lock the sink so we can safely get it's properties and call 604 * down into the audio driver to create that end of the stream. 605 */ 606 rc = RTCritSectEnter(&pSink->CritSect); 607 AssertRC(rc); 606 608 if (RT_SUCCESS(rc)) 607 609 { 608 pMixStream->StatsCircBufSize = (uint32_t)RTCircBufSize(pMixStream->pCircBuf);609 pMixStream->StatsCircBufUsed = (uint32_t)RTCircBufUsed(pMixStream->pCircBuf);610 LogFlowFunc(("[%s] fFlags=0x%x (enmDir=%ld, %u bits, %RU8 channels, %RU32Hz)\n", pSink->pszName, fFlags, pCfg->enmDir, 611 PDMAudioPropsSampleBits(&pCfg->Props), PDMAudioPropsChannels(&pCfg->Props), pCfg->Props.uHz)); 610 612 611 613 /* 612 * Lock the sink so we can safely get it's properties and call613 * down into the audio driver to create that end of the stream.614 * Initialize the host-side configuration for the stream to be created. 615 * Always use the sink's PCM audio format as the host side when creating a stream for it. 614 616 */ 615 rc = RTCritSectEnter(&pSink->CritSect); 616 AssertRC(rc); 617 AssertMsg(AudioHlpPcmPropsAreValid(&pSink->PCMProps), 618 ("%s: Does not (yet) have a format set when it must\n", pSink->pszName)); 619 620 PDMAUDIOSTREAMCFG CfgHost; 621 rc = PDMAudioStrmCfgInitWithProps(&CfgHost, &pSink->PCMProps); 622 AssertRC(rc); /* cannot fail */ 623 624 /* Apply the sink's direction for the configuration to use to create the stream. */ 625 if (pSink->enmDir == AUDMIXSINKDIR_INPUT) 626 { 627 CfgHost.enmDir = PDMAUDIODIR_IN; 628 CfgHost.u.enmSrc = pCfg->u.enmSrc; 629 CfgHost.enmLayout = pCfg->enmLayout; 630 } 631 else 632 { 633 CfgHost.enmDir = PDMAUDIODIR_OUT; 634 CfgHost.u.enmDst = pCfg->u.enmDst; 635 CfgHost.enmLayout = pCfg->enmLayout; 636 } 637 638 RTStrCopy(CfgHost.szName, sizeof(CfgHost.szName), pCfg->szName); 639 640 /* 641 * Create the stream. 642 * 643 * Output streams are not using any mixing buffers in DrvAudio. This will 644 * become the norm after we move the input mixing here and convert DevSB16 645 * to use this mixer code too. 646 */ 647 PPDMAUDIOSTREAM pStream; 648 rc = pConn->pfnStreamCreate(pConn, pSink->enmDir == AUDMIXSINKDIR_OUTPUT ? PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF : 0, 649 &CfgHost, pCfg, &pStream); 617 650 if (RT_SUCCESS(rc)) 618 651 { 619 LogFlowFunc(("[%s] fFlags=0x%x (enmDir=%ld, %u bits, %RU8 channels, %RU32Hz)\n", pSink->pszName, fFlags, pCfg->enmDir, 620 PDMAudioPropsSampleBits(&pCfg->Props), PDMAudioPropsChannels(&pCfg->Props), pCfg->Props.uHz)); 621 622 /* 623 * Initialize the host-side configuration for the stream to be created. 624 * Always use the sink's PCM audio format as the host side when creating a stream for it. 625 */ 626 AssertMsg(AudioHlpPcmPropsAreValid(&pSink->PCMProps), 627 ("%s: Does not (yet) have a format set when it must\n", pSink->pszName)); 628 629 PDMAUDIOSTREAMCFG CfgHost; 630 rc = PDMAudioStrmCfgInitWithProps(&CfgHost, &pSink->PCMProps); 631 AssertRC(rc); /* cannot fail */ 632 633 /* Apply the sink's direction for the configuration to use to create the stream. */ 634 if (pSink->enmDir == AUDMIXSINKDIR_INPUT) 635 { 636 CfgHost.enmDir = PDMAUDIODIR_IN; 637 CfgHost.u.enmSrc = pCfg->u.enmSrc; 638 CfgHost.enmLayout = pCfg->enmLayout; 639 } 640 else 641 { 642 CfgHost.enmDir = PDMAUDIODIR_OUT; 643 CfgHost.u.enmDst = pCfg->u.enmDst; 644 CfgHost.enmLayout = pCfg->enmLayout; 645 } 646 647 RTStrCopy(CfgHost.szName, sizeof(CfgHost.szName), pCfg->szName); 648 649 /* 650 * Create the stream. 651 */ 652 PPDMAUDIOSTREAM pStream; 653 rc = pConn->pfnStreamCreate(pConn, 0 /*fFlags*/, &CfgHost, pCfg, &pStream); 652 /* Set up the mixing buffer conversion state. */ 653 if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT) 654 rc = AudioMixBufInitPeekState(&pSink->MixBuf, &pMixStream->PeekState, &pStream->Props); 654 655 if (RT_SUCCESS(rc)) 655 656 { … … 664 665 RTCritSectLeave(&pSink->CritSect); 665 666 666 /*667 * Register statistics before we return.668 */669 PDMDevHlpSTAMRegisterF(pDevIns, &pMixStream->StatsCircBufSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,670 "Circular buffer size", "%sCircBufSize", pMixStream->pszStatPrefix);671 PDMDevHlpSTAMRegisterF(pDevIns, &pMixStream->StatsCircBufUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,672 "Circular buffer fill size", "%sCircBufUsed", pMixStream->pszStatPrefix);673 674 667 if (ppStream) 675 668 *ppStream = pMixStream; … … 677 670 } 678 671 679 /* 680 * Failed. Tear down the stream. 681 */ 682 int rc2 = RTCritSectLeave(&pSink->CritSect); 683 AssertRC(rc2); 672 rc = pConn->pfnStreamDestroy(pConn, pStream); 684 673 } 685 RTCircBufDestroy(pMixStream->pCircBuf); 674 675 /* 676 * Failed. Tear down the stream. 677 */ 678 int rc2 = RTCritSectLeave(&pSink->CritSect); 679 AssertRC(rc2); 686 680 } 687 681 RTCritSectDelete(&pMixStream->CritSect); … … 1650 1644 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME)); 1651 1645 1652 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU8, rVol=%RU8\n",1653 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));1654 1655 1646 LogRel2(("Audio Mixer: Setting volume of sink '%s' to %RU8/%RU8 (%s)\n", 1656 1647 pSink->pszName, pVol->uLeft, pVol->uRight, pVol->fMuted ? "Muted" : "Unmuted")); … … 1666 1657 1667 1658 /** 1668 * Updates a mixer sink, internal version.1659 * Updates an input mixer sink. 1669 1660 * 1670 1661 * @returns VBox status code. 1671 1662 * @param pSink Mixer sink to update. 1672 1663 */ 1673 static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink) 1664 static int audioMixerSinkUpdateInput(PAUDMIXSINK pSink) 1665 { 1666 /* 1667 * Warning! We currently do _not_ use the mixing buffer for input streams! 1668 * Warning! We currently do _not_ use the mixing buffer for input streams! 1669 * Warning! We currently do _not_ use the mixing buffer for input streams! 1670 */ 1671 1672 /* 1673 * Skip input sinks without a recoring source. 1674 */ 1675 if (pSink->In.pStreamRecSource == NULL) 1676 return VINF_SUCCESS; 1677 1678 /* 1679 * Update each mixing sink stream's status. 1680 */ 1681 PAUDMIXSTREAM pMixStream; 1682 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1683 { 1684 int rc2 = audioMixerStreamUpdateStatus(pMixStream); 1685 AssertRC(rc2); 1686 } 1687 1688 /* 1689 * Iterate and do capture on the recording source. We ignore all other streams. 1690 */ 1691 int rc = VINF_SUCCESS; /* not sure if error propagation is worth it... */ 1692 #if 1 1693 pMixStream = pSink->In.pStreamRecSource; 1694 #else 1695 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1696 #endif 1697 { 1698 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 1699 { 1700 uint32_t cFramesCaptured = 0; 1701 int rc2 = pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pMixStream->pStream); 1702 if (RT_SUCCESS(rc2)) 1703 { 1704 rc2 = pMixStream->pConn->pfnStreamCapture(pMixStream->pConn, pMixStream->pStream, &cFramesCaptured); 1705 if (RT_SUCCESS(rc2)) 1706 { 1707 if (cFramesCaptured) 1708 pSink->fStatus |= AUDMIXSINK_STS_DIRTY; 1709 } 1710 else 1711 { 1712 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2)); 1713 if (RT_SUCCESS(rc)) 1714 rc = rc2; 1715 } 1716 } 1717 else if (RT_SUCCESS(rc)) 1718 rc = rc2; 1719 Log3Func(("%s: cFramesCaptured=%RU32 (rc2=%Rrc)\n", pMixStream->pStream->szName, cFramesCaptured, rc2)); 1720 } 1721 } 1722 1723 /* Update last updated timestamp. */ 1724 pSink->tsLastUpdatedMs = RTTimeMilliTS(); 1725 1726 /* 1727 * Deal with pending disable. The general case is that we reset 1728 * the sink when all streams have been disabled, however input is 1729 * currently a special case where we only care about the one 1730 * recording source... 1731 */ 1732 if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE) 1733 { 1734 #if 1 1735 uint32_t const cStreams = 1; 1736 uint32_t cStreamsDisabled = 1; 1737 pMixStream = pSink->In.pStreamRecSource; 1738 #else 1739 uint32_t const cStreams = pSink->cStreams; 1740 uint32_t cStreamsDisabled = pSink->cStreams; 1741 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1742 #endif 1743 { 1744 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 1745 { 1746 PDMAUDIOSTREAMSTS const fSts = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream); 1747 if (fSts & (PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)) 1748 cStreamsDisabled--; 1749 } 1750 } 1751 Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams)); 1752 if (cStreamsDisabled == cStreams) 1753 audioMixerSinkReset(pSink); 1754 } 1755 1756 return rc; 1757 } 1758 1759 /** 1760 * Updates an output mixer sink. 1761 * 1762 * @returns VBox status code. 1763 * @param pSink Mixer sink to update. 1764 */ 1765 static int audioMixerSinkUpdateOutput(PAUDMIXSINK pSink) 1766 { 1767 /* 1768 * Update each mixing sink stream's status and check how much we can 1769 * write into them. 1770 * 1771 * We're currently using the minimum size of all streams, however this 1772 * isn't a smart approach as it means one disfunctional stream can block 1773 * working ones. 1774 */ 1775 /** @todo rework this so a broken stream cannot hold up everyone. */ 1776 uint32_t cFramesToRead = AudioMixBufLive(&pSink->MixBuf); /* (to read from the mixing buffer) */ 1777 uint32_t cWritableStreams = 0; 1778 PAUDMIXSTREAM pMixStream; 1779 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1780 { 1781 #if 0 /** @todo this conceptually makes sense, but may mess up the pending-disable logic ... */ 1782 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 1783 pConn->pfnStreamIterate(pConn, pStream); 1784 #endif 1785 1786 int rc2 = audioMixerStreamUpdateStatus(pMixStream); 1787 AssertRC(rc2); 1788 1789 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 1790 { 1791 uint32_t const cbWritable = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream); 1792 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pMixStream->pStream->Props, cbWritable); 1793 if (PDMAudioPropsHz(&pMixStream->pStream->Props) == PDMAudioPropsHz(&pSink->MixBuf.Props)) 1794 { /* likely */ } 1795 else 1796 { 1797 cFrames = cFrames * PDMAudioPropsHz(&pSink->MixBuf.Props) / PDMAudioPropsHz(&pMixStream->pStream->Props); 1798 cFrames = cFrames > 2 ? cFrames - 2 : 0; /* rounding safety fudge */ 1799 } 1800 if (cFramesToRead > cFrames) 1801 { 1802 Log4Func(("%s: cFramesToRead %u -> %u; %s (%u bytes writable)\n", 1803 pSink->pszName, cFramesToRead, cFrames, pMixStream->pszName, cbWritable)); 1804 cFramesToRead = cFrames; 1805 } 1806 cWritableStreams++; 1807 } 1808 } 1809 Log3Func(("%s: cLiveFrames=%#x cFramesToRead=%#x cWritableStreams=%#x\n", pSink->pszName, 1810 AudioMixBufLive(&pSink->MixBuf), cFramesToRead, cWritableStreams)); 1811 1812 if (cWritableStreams) 1813 { 1814 /* 1815 * For each of the enabled streams, convert cFramesToRead frames from 1816 * the mixing buffer and write that to the downstream driver. 1817 */ 1818 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1819 { 1820 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 1821 { 1822 uint32_t offSrcFrame = 0; 1823 do 1824 { 1825 /* Convert a chunk from the mixer buffer. */ 1826 union 1827 { 1828 uint8_t ab[8192]; 1829 uint64_t au64[8192 / sizeof(uint64_t)]; /* Use uint64_t to ensure good alignment. */ 1830 } Buf; 1831 uint32_t cbDstPeeked = sizeof(Buf); 1832 uint32_t cSrcFramesPeeked = cFramesToRead - offSrcFrame; 1833 AudioMixBufPeek(&pSink->MixBuf, offSrcFrame, cSrcFramesPeeked, &cSrcFramesPeeked, 1834 &pMixStream->PeekState, &Buf, sizeof(Buf), &cbDstPeeked); 1835 offSrcFrame += cSrcFramesPeeked; 1836 1837 /* Write it to the backend. Since've checked that there is buffer 1838 space available, this should always write the whole buffer. */ 1839 uint32_t cbDstWritten = 0; 1840 int rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, 1841 &Buf, cbDstPeeked, &cbDstWritten); 1842 Log3Func(("%s: %#x L %#x => %#x bytes; wrote %#x rc2=%Rrc %s\n", pSink->pszName, offSrcFrame, 1843 cSrcFramesPeeked - cSrcFramesPeeked, cbDstPeeked, cbDstWritten, rc2, pMixStream->pszName)); 1844 if (RT_SUCCESS(rc2)) 1845 AssertLogRelMsg(cbDstWritten == cbDstPeeked, 1846 ("cbDstWritten=%#x cbDstPeeked=%#x - (sink '%s')\n", 1847 cbDstWritten, cbDstPeeked, pSink->pszName)); 1848 else if (rc2 == VERR_AUDIO_STREAM_NOT_READY) 1849 { 1850 LogRel2(("Audio Mixer: '%s' (sink '%s'): Stream not ready - skipping.\n", pMixStream->pszName, pSink->pszName)); 1851 break; /* must've changed status, stop processing */ 1852 } 1853 else 1854 { 1855 Assert(rc2 != VERR_BUFFER_OVERFLOW); 1856 LogRel2(("Audio Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n", 1857 pMixStream->pszName, pSink->pszName, rc2)); 1858 break; 1859 } 1860 } while (offSrcFrame < cFramesToRead); 1861 Assert(offSrcFrame == cFramesToRead); 1862 } 1863 } 1864 1865 AudioMixBufAdvance(&pSink->MixBuf, cFramesToRead); 1866 1867 /* 1868 * Update the dirty flag for what it's worth. 1869 */ 1870 if (AudioMixBufUsed(&pSink->MixBuf)) 1871 pSink->fStatus |= AUDMIXSINK_STS_DIRTY; 1872 else 1873 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY; 1874 } 1875 else 1876 { 1877 /* 1878 * If no writable streams, just drop the mixer buffer content. 1879 */ 1880 AudioMixBufDrop(&pSink->MixBuf); 1881 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY; 1882 } 1883 1884 /* 1885 * Iterate buffers (pfnStreamPlay is not used any more). 1886 */ 1887 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1888 { 1889 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 1890 pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pMixStream->pStream); 1891 } 1892 1893 /* Update last updated timestamp. */ 1894 pSink->tsLastUpdatedMs = RTTimeMilliTS(); 1895 1896 /* 1897 * Deal with pending disable. 1898 * We reset the sink when all streams have been disabled. 1899 */ 1900 if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE) 1901 { 1902 uint32_t const cStreams = pSink->cStreams; 1903 uint32_t cStreamsDisabled = pSink->cStreams; 1904 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1905 { 1906 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 1907 { 1908 PDMAUDIOSTREAMSTS const fSts = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream); 1909 if (fSts & (PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)) 1910 cStreamsDisabled--; 1911 } 1912 } 1913 Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams)); 1914 if (cStreamsDisabled == cStreams) 1915 audioMixerSinkReset(pSink); 1916 } 1917 1918 return VINF_SUCCESS; 1919 } 1920 1921 /** 1922 * Updates (invalidates) a mixer sink. 1923 * 1924 * @returns VBox status code. 1925 * @param pSink Mixer sink to update. 1926 */ 1927 int AudioMixerSinkUpdate(PAUDMIXSINK pSink) 1674 1928 { 1675 1929 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 1930 int rc = RTCritSectEnter(&pSink->CritSect); 1931 AssertRCReturn(rc, rc); 1676 1932 1677 1933 #ifdef LOG_ENABLED … … 1680 1936 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus))); 1681 1937 1682 /* Sink disabled? Take a shortcut. */ 1683 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING)) 1684 return VINF_SUCCESS; 1685 1686 /* Input sink and no recording source set? Bail out early. */ 1687 if ( pSink->enmDir == AUDMIXSINKDIR_INPUT 1688 && pSink->In.pStreamRecSource == NULL) 1689 return VINF_SUCCESS; 1690 1691 /* Sanity. */ 1692 AssertPtr(pSink->pabScratchBuf); 1693 Assert(pSink->cbScratchBuf); 1694 1695 /* Update each mixing sink stream's status. */ 1696 PAUDMIXSTREAM pMixStream; 1697 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1698 { 1699 int rc2 = audioMixerStreamUpdateStatus(pMixStream); 1700 AssertRC(rc2); 1701 } 1702 1703 /* Number of disabled streams of this sink. */ 1704 uint8_t cStreamsDisabled = pSink->cStreams; 1705 1706 /* Next, try to write (multiplex) as much audio data as possible to all connected mixer streams. */ 1707 uint32_t cbToWriteToStreams = AudioMixBufUsedBytes(&pSink->MixBuf); 1708 1709 int rc = VINF_SUCCESS; 1710 while (cbToWriteToStreams) 1711 { 1712 uint32_t cfChunk; 1713 rc = AudioMixBufAcquireReadBlock(&pSink->MixBuf, pSink->pabScratchBuf, 1714 RT_MIN(cbToWriteToStreams, (uint32_t)pSink->cbScratchBuf), &cfChunk); 1715 if (RT_FAILURE(rc)) 1716 break; 1717 1718 const uint32_t cbChunk = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cfChunk); 1719 Assert(cbChunk <= pSink->cbScratchBuf); 1720 1721 /* Multiplex the current chunk in a synchronized fashion to all connected streams. */ 1722 uint32_t cbChunkWrittenMin = 0; 1723 rc = audioMixerSinkMultiplexSync(pSink, pSink->pabScratchBuf, cbChunk, &cbChunkWrittenMin); 1724 if (RT_SUCCESS(rc)) 1725 { 1726 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1727 { 1728 int rc2 = audioMixerSinkWriteToStream(pSink, pMixStream); 1729 AssertRC(rc2); 1730 } 1731 } 1732 1733 Log3Func(("[%s] cbChunk=%RU32, cbChunkWrittenMin=%RU32\n", pSink->pszName, cbChunk, cbChunkWrittenMin)); 1734 1735 AudioMixBufReleaseReadBlock(&pSink->MixBuf, AUDIOMIXBUF_B2F(&pSink->MixBuf, cbChunkWrittenMin)); 1736 1737 if ( RT_FAILURE(rc) 1738 || cbChunkWrittenMin == 0) 1739 break; 1740 1741 Assert(cbToWriteToStreams >= cbChunkWrittenMin); 1742 cbToWriteToStreams -= cbChunkWrittenMin; 1743 } 1744 1745 if ( !(pSink->fStatus & AUDMIXSINK_STS_DIRTY) 1746 && AudioMixBufUsed(&pSink->MixBuf)) /* Still audio output data left? Consider the sink as being "dirty" then. */ 1747 { 1748 /* Set dirty bit. */ 1749 pSink->fStatus |= AUDMIXSINK_STS_DIRTY; 1750 } 1751 1752 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1753 { 1754 /* Input sink and not the recording source? Skip. */ 1755 if ( pSink->enmDir == AUDMIXSINKDIR_INPUT 1756 && pSink->In.pStreamRecSource != pMixStream) 1757 continue; 1758 1759 PPDMAUDIOSTREAM pStream = pMixStream->pStream; 1760 AssertPtr(pStream); 1761 1762 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn; 1763 AssertPtr(pConn); 1764 1765 uint32_t cfProc = 0; 1766 1767 if (!(pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)) 1768 continue; 1769 1770 int rc2 = pConn->pfnStreamIterate(pConn, pStream); 1771 if (RT_SUCCESS(rc2)) 1772 { 1773 if (pSink->enmDir == AUDMIXSINKDIR_INPUT) 1774 { 1775 rc2 = pConn->pfnStreamCapture(pConn, pStream, &cfProc); 1776 if (RT_FAILURE(rc2)) 1777 { 1778 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2)); 1779 continue; 1780 } 1781 1782 if (cfProc) 1783 pSink->fStatus |= AUDMIXSINK_STS_DIRTY; 1784 } 1785 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT) 1786 { 1787 rc2 = pConn->pfnStreamPlay(pConn, pStream, &cfProc); 1788 if (RT_FAILURE(rc2)) 1789 { 1790 LogFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2)); 1791 continue; 1792 } 1793 /** @todo r=bird: Maybe a pfnStreamIterate call here? It's a bit risky wrt 1794 * prematurely disabling the stream. */ 1795 } 1796 else 1797 { 1798 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED); 1799 continue; 1800 } 1801 } 1802 1803 const PDMAUDIOSTREAMSTS fStreamStatusNew = pConn->pfnStreamGetStatus(pConn, pStream); 1804 1805 /* Is the stream enabled or in pending disable state? 1806 * Don't consider this stream as being disabled then. */ 1807 if (fStreamStatusNew & (PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)) 1808 cStreamsDisabled--; 1809 /* Note: The mixer stream's internal status will be updated in the next iteration of this function. */ 1810 1811 Log3Func(("\t%s: cPlayed/cCaptured=%RU32, rc2=%Rrc\n", pStream->szName, cfProc, rc2)); 1812 } 1813 1814 Log3Func(("[%s] fPendingDisable=%RTbool, %RU8/%RU8 streams disabled\n", 1815 pSink->pszName, RT_BOOL(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE), cStreamsDisabled, pSink->cStreams)); 1816 1817 /* Update last updated timestamp. */ 1818 pSink->tsLastUpdatedMs = RTTimeMilliTS(); 1819 1820 /* All streams disabled and the sink is in pending disable mode? */ 1821 if ( cStreamsDisabled == pSink->cStreams 1822 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE)) 1823 { 1824 audioMixerSinkReset(pSink); 1825 } 1826 1827 return rc; 1828 } 1829 1830 /** 1831 * Updates (invalidates) a mixer sink. 1832 * 1833 * @returns VBox status code. 1834 * @param pSink Mixer sink to update. 1835 */ 1836 int AudioMixerSinkUpdate(PAUDMIXSINK pSink) 1837 { 1838 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 1839 1840 int rc = RTCritSectEnter(&pSink->CritSect); 1841 if (RT_FAILURE(rc)) 1842 return rc; 1843 1844 rc = audioMixerSinkUpdateInternal(pSink); 1845 1846 int rc2 = RTCritSectLeave(&pSink->CritSect); 1847 AssertRC(rc2); 1848 1938 /* Only process running sinks. */ 1939 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING) 1940 { 1941 /* Do separate processing for input and output sinks. */ 1942 if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT) 1943 rc = audioMixerSinkUpdateOutput(pSink); 1944 else if (pSink->enmDir == AUDMIXSINKDIR_INPUT) 1945 rc = audioMixerSinkUpdateInput(pSink); 1946 else 1947 AssertFailed(); 1948 } 1949 else 1950 rc = VINF_SUCCESS; /* disabled */ 1951 1952 RTCritSectLeave(&pSink->CritSect); 1849 1953 return rc; 1850 1954 } … … 1880 1984 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight)); 1881 1985 1882 /* Propagate new sink volume to all streams in the sink. */ 1883 PAUDMIXSTREAM pMixStream; 1884 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1885 { 1886 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined); 1887 AssertRC(rc2); 1986 /* 1987 * Input sinks must currently propagate the new volume settings to 1988 * all the streams. (For output sinks we do the volume control here.) 1989 */ 1990 if (pSink->enmDir != AUDMIXSINKDIR_OUTPUT) 1991 { 1992 PAUDMIXSTREAM pMixStream; 1993 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1994 { 1995 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined); 1996 AssertRC(rc2); 1997 } 1888 1998 } 1889 1999 … … 1892 2002 1893 2003 /** 1894 * Writes (buffered) output data of a sink's stream to the bound audio connector stream. 1895 * 1896 * @returns VBox status code. 1897 * @param pSink Sink of stream that contains the mixer stream. 1898 * @param pMixStream Mixer stream to write output data for. 1899 */ 1900 static int audioMixerSinkWriteToStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream) 1901 { 1902 if (!pMixStream->pCircBuf) 1903 return VINF_SUCCESS; 1904 1905 return audioMixerSinkWriteToStreamEx(pSink, pMixStream, (uint32_t)RTCircBufUsed(pMixStream->pCircBuf), NULL /* pcbWritten */); 1906 } 1907 1908 /** 1909 * Writes (buffered) output data of a sink's stream to the bound audio connector stream, extended version. 1910 * 1911 * @returns VBox status code. 1912 * @param pSink Sink of stream that contains the mixer stream. 1913 * @param pMixStream Mixer stream to write output data for. 1914 * @param cbToWrite Size (in bytes) to write. 1915 * @param pcbWritten Size (in bytes) written on success. Optional. 1916 */ 1917 static int audioMixerSinkWriteToStreamEx(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream, uint32_t cbToWrite, uint32_t *pcbWritten) 1918 { 1919 /* pcbWritten is optional. */ 1920 1921 if ( !cbToWrite 1922 || !(pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)) 1923 { 1924 if (pcbWritten) 1925 *pcbWritten = 0; 1926 1927 return VINF_SUCCESS; 1928 } 1929 1930 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf; 1931 1932 const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream); 1933 cbToWrite = RT_MIN(cbToWrite, RT_MIN((uint32_t)RTCircBufUsed(pCircBuf), cbWritableStream)); 1934 1935 Log3Func(("[%s] cbWritableStream=%RU32, cbToWrite=%RU32\n", pMixStream->pszName, cbWritableStream, cbToWrite)); 1936 1937 uint32_t cbWritten = 0; 1938 1939 int rc = VINF_SUCCESS; 1940 1941 while (cbToWrite) 1942 { 1943 void *pvChunk; 1944 size_t cbChunk; 1945 RTCircBufAcquireReadBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk); 1946 1947 Log3Func(("[%s] cbChunk=%RU32\n", pMixStream->pszName, cbChunk)); 1948 1949 uint32_t cbChunkWritten = 0; 1950 if (cbChunk) 1951 { 1952 rc = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk, 1953 &cbChunkWritten); 1954 if (RT_FAILURE(rc)) 1955 { 1956 if (rc == VERR_BUFFER_OVERFLOW) 1957 { 1958 LogRel2(("Audio Mixer: Buffer overrun for mixer stream '%s' (sink '%s')\n", pMixStream->pszName, pSink->pszName)); 1959 break; 1960 } 1961 if (rc == VERR_AUDIO_STREAM_NOT_READY) 1962 { 1963 /* Stream is not enabled, just skip. */ 1964 rc = VINF_SUCCESS; 1965 } 1966 else 1967 LogRel2(("Audio Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n", 1968 pMixStream->pszName, pSink->pszName, rc)); 1969 1970 if (RT_FAILURE(rc)) 1971 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc)); 1972 } 1973 } 1974 1975 RTCircBufReleaseReadBlock(pCircBuf, cbChunkWritten); 1976 1977 if ( RT_FAILURE(rc) 1978 || !cbChunkWritten) 1979 break; 1980 1981 Assert(cbToWrite >= cbChunkWritten); 1982 cbToWrite -= (uint32_t)cbChunkWritten; 1983 1984 cbWritten += (uint32_t)cbChunkWritten; 1985 } 1986 1987 pMixStream->StatsCircBufUsed = (uint32_t)RTCircBufUsed(pCircBuf); 1988 Log3Func(("[%s] cbWritten=%RU32\n", pMixStream->pszName, cbWritten)); 1989 1990 if (pcbWritten) 1991 *pcbWritten = cbWritten; 1992 1993 #ifdef DEBUG_andy 1994 AssertRC(rc); 1995 #endif 1996 1997 return rc; 1998 } 1999 2000 /** 2001 * Multiplexes audio output data to all connected mixer streams in a synchronized fashion, e.g. 2002 * only multiplex as much data as all streams can handle at this time. 2003 * 2004 * @returns VBox status code. 2005 * @param pSink Sink to write audio output to. 2006 * @param pvBuf Pointer to audio data to write. 2007 * @param cbBuf Size (in bytes) of audio data to write. 2008 * @param pcbWritten Returns the number of bytes written to each of the 2009 * streams. 2010 */ 2011 static int audioMixerSinkMultiplexSync(PAUDMIXSINK pSink, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 2012 { 2013 Log3Func(("[%s] cbBuf=%RU32\n", pSink->pszName, cbBuf)); 2014 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 2015 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT, 2016 ("%s: Can't multiplex to a sink which is not an output sink\n", pSink->pszName)); 2017 2018 /* 2019 * Check all enabled streems for buffer space. 2020 */ 2021 uint32_t cbToWrite = UINT32_MAX; 2022 PAUDMIXSTREAM pMixStream; 2023 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 2024 { 2025 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 2026 { 2027 uint32_t const cbCircFree = (uint32_t)RTCircBufFree(pMixStream->pCircBuf); 2028 cbToWrite = RT_MIN(cbToWrite, cbCircFree); 2029 } 2030 else 2031 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName)); 2032 } 2033 if (cbToWrite != UINT32_MAX) 2034 cbToWrite = RT_MIN(cbBuf, cbToWrite); 2035 else 2036 cbToWrite = 0; /* No active streams at all. */ 2037 if (cbToWrite) 2038 { 2039 /* 2040 * Do the copying. 2041 */ 2042 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 2043 { 2044 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 2045 { 2046 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf; 2047 size_t offWritten = 0; 2048 while (offWritten < cbToWrite) 2049 { 2050 void *pvChunk = NULL; 2051 size_t cbChunk = 0; 2052 RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite - offWritten, &pvChunk, &cbChunk); 2053 2054 memcpy(pvChunk, (uint8_t const *)pvBuf + offWritten, cbChunk); 2055 2056 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk); 2057 2058 offWritten += cbChunk; 2059 } 2060 Assert(offWritten == cbToWrite); 2061 2062 pMixStream->tsLastReadWrittenNs = RTTimeNanoTS(); 2063 pMixStream->StatsCircBufUsed = (uint32_t)RTCircBufUsed(pCircBuf); 2064 Log3Func(("[%s] Mixer stream '%s' -> cbWrittenBuf=%RU32\n", pSink->pszName, pMixStream->pszName, cbToWrite)); 2065 } 2066 } 2067 } 2068 2069 Log3Func(("[%s] cbBuf=%RU32, cbToWrite=%RU32\n", pSink->pszName, cbBuf, cbToWrite)); 2070 2071 *pcbWritten = cbToWrite; 2072 return VINF_SUCCESS; 2073 } 2074 2075 /** 2076 * Writes data to a mixer sink. 2004 * Writes data to a mixer output sink. 2077 2005 * 2078 2006 * @returns VBox status code. … … 2130 2058 } 2131 2059 2060 2132 2061 /********************************************************************************************************************************* 2133 2062 * Mixer Stream implementation. … … 2258 2187 pMixStream->pszName = NULL; 2259 2188 2260 if (pMixStream->pCircBuf)2261 {2262 RTCircBufDestroy(pMixStream->pCircBuf);2263 pMixStream->pCircBuf = NULL;2264 }2265 2266 2189 int rc2 = RTCritSectDelete(&pMixStream->CritSect); 2267 2190 AssertRC(rc2); … … 2378 2301 return fIsValid; 2379 2302 } 2303 -
trunk/src/VBox/Devices/Audio/AudioMixer.h
r88357 r88433 91 91 /** The statistics prefix. */ 92 92 char *pszStatPrefix; 93 /** The streams's critical section. */94 RTCRITSECT CritSect;95 93 /** Sink this stream is attached to. */ 96 94 PAUDMIXSINK pSink; … … 103 101 /** Pointer to PDM audio stream this mixer stream handles. */ 104 102 PPDMAUDIOSTREAM pStream; 103 /** Mixing buffer peeking state & config. */ 104 AUDIOMIXBUFPEEKSTATE PeekState; 105 105 /** Last read (recording) / written (playback) timestamp (in ns). */ 106 106 uint64_t tsLastReadWrittenNs; 107 /** The stream's circular buffer for temporarily 108 * holding (raw) device audio data. */ 109 PRTCIRCBUF pCircBuf; 110 /** Stats: Number of bytes used in the circular buffer. */ 111 uint32_t StatsCircBufUsed; 112 /** Stats: Size of circular buffer. */ 113 uint32_t StatsCircBufSize; 107 /** The streams's critical section. */ 108 RTCRITSECT CritSect; 114 109 } AUDMIXSTREAM, *PAUDMIXSTREAM; 115 110 … … 124 119 #define AUDMIXSINK_STS_PENDING_DISABLE RT_BIT(1) 125 120 /** Dirty flag. 126 * For output sinks this means that there is data in the 127 * sink which has not been played yet. 128 * For input sinks this means that there is data in the 129 * sink which has been recorded but not transferred to the 130 * destination yet. */ 121 * - For output sinks this means that there is data in the sink which has not 122 * been played yet. 123 * - For input sinks this means that there is data in the sink which has been 124 * recorded but not transferred to the destination yet. */ 131 125 #define AUDMIXSINK_STS_DIRTY RT_BIT(2) 132 126 133 127 /** 134 128 * Audio mixer sink direction. 129 * @todo r=bird: use PDMAUDIODIR instead. 135 130 */ 136 131 typedef enum AUDMIXSINKDIR -
trunk/src/VBox/Devices/Audio/DrvAudio.cpp
r88412 r88433 128 128 * write (output streams). */ 129 129 uint64_t nsLastReadWritten; 130 /** Internal stream position (as per pfnStreamWrite/Read). */ 131 uint64_t offInternal; 130 132 131 133 … … 179 181 uint32_t cbBackendWritableAfter; 180 182 } Stats; 183 184 /** Space for pre-buffering. */ 185 uint8_t *pbPreBuf; 186 /** The size of the pre-buffer allocation (in bytes). */ 187 uint32_t cbPreBufAlloc; 188 /** Number of bytes we've prebuffered. */ 189 uint32_t cbPreBuffered; 190 /** The pre-buffering threshold expressed in bytes. */ 191 uint32_t cbPreBufThreshold; 192 181 193 /** Hack alert: Max writable amount reported by the backend. 182 194 * This is used to aid buffer underrun detection in DrvAudio while playing. … … 958 970 LogFunc(("[%s]\n", pStreamEx->Core.szName)); 959 971 960 AudioMixBufReset(&pStreamEx->Guest.MixBuf); 961 AudioMixBufReset(&pStreamEx->Host.MixBuf); 972 if (pStreamEx->fNoMixBufs) 973 { 974 AudioMixBufReset(&pStreamEx->Guest.MixBuf); 975 AudioMixBufReset(&pStreamEx->Host.MixBuf); 976 } 962 977 963 978 pStreamEx->fThresholdReached = false; … … 1004 1019 #endif 1005 1020 } 1021 1022 1023 /** 1024 * The no-mixing-buffers worker for drvAudioStreamWrite and 1025 * drvAudioStreamIterateInternal. 1026 * 1027 * The buffer is NULL and has a zero length when called from the interate 1028 * function. This only occures when there is pre-buffered audio data that need 1029 * to be pushed to the backend due to a pending disabling of the stream. 1030 * 1031 * Caller owns the lock. 1032 */ 1033 static int drvAudioStreamWriteNoMixBufs(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, 1034 const uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbWritten) 1035 { 1036 Log3Func(("%s: @%#RX64: cbBuf=%#x\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbBuf)); 1037 1038 /* 1039 * Are we pre-buffering? 1040 * 1041 * Note! We do not restart pre-buffering in this version, as we'd 1042 * need some kind of cooperation with the backend buffer 1043 * managment to correctly detect an underrun. 1044 */ 1045 bool fJustStarted = false; 1046 uint32_t cbWritten = 0; 1047 int rc; 1048 if ( pStreamEx->fThresholdReached 1049 && pStreamEx->Out.cbPreBuffered == 0) 1050 { 1051 /* not-prebuffering, likely after a while at least */ 1052 rc = VINF_SUCCESS; 1053 } 1054 else 1055 { 1056 /* 1057 * Copy as much as we can to the pre-buffer. 1058 */ 1059 uint32_t cbFree = pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered; 1060 AssertReturn((int32_t)cbFree >= 0, VERR_INTERNAL_ERROR_2); 1061 if (cbFree > 0 && cbBuf > 0) 1062 { 1063 cbWritten = RT_MIN(cbFree, cbBuf); 1064 cbWritten = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Props, cbWritten); 1065 memcpy(&pStreamEx->Out.pbPreBuf[pStreamEx->Out.cbPreBuffered], pbBuf, cbWritten); 1066 pStreamEx->Out.cbPreBuffered += cbWritten; 1067 cbBuf -= cbWritten; 1068 pbBuf += cbWritten; 1069 pStreamEx->offInternal += cbWritten; 1070 } 1071 1072 /* 1073 * Get the special case of buggy backend drivers out of the way. 1074 * We get here if we couldn't write out all the pre-buffered data when 1075 * we hit the threshold. 1076 */ 1077 if (pStreamEx->fThresholdReached) 1078 LogRel2(("Audio: @%#RX64: Stream '%s' pre-buffering commit problem: cbBuf=%#x cbPreBuffered=%#x\n", 1079 pStreamEx->offInternal, pStreamEx->Core.szName, cbBuf, pStreamEx->Out.cbPreBuffered)); 1080 /* 1081 * Did we reach the backend's playback (pre-buffering) threshold? 1082 * Can be 0 if no pre-buffering desired. 1083 */ 1084 else if (pStreamEx->Out.cbPreBuffered + cbBuf >= pStreamEx->Out.cbPreBufThreshold) 1085 { 1086 LogRel2(("Audio: @%#RX64: Stream '%s' buffering complete! (%#x + %#x bytes)\n", 1087 pStreamEx->offInternal, pStreamEx->Core.szName, pStreamEx->Out.cbPreBuffered, cbBuf)); 1088 pStreamEx->fThresholdReached = fJustStarted = true; 1089 } 1090 /* 1091 * Some audio files are shorter than the pre-buffering level (e.g. the 1092 * "click" Explorer sounds on some Windows guests), so make sure that we 1093 * also play those by checking if the stream already is pending disable 1094 * mode, even if we didn't hit the pre-buffering watermark yet. 1095 * 1096 * Try play "Windows Navigation Start.wav" on Windows 7 (2824 samples). 1097 */ 1098 else if ( (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE) 1099 && pStreamEx->Out.cbPreBuffered > 0) 1100 { 1101 LogRel2(("Audio: @%#RX64: Stream '%s' buffering complete - short sound! (%#x + %#x bytes)\n", 1102 pStreamEx->offInternal, pStreamEx->Core.szName, pStreamEx->Out.cbPreBuffered, cbBuf)); 1103 pStreamEx->fThresholdReached = fJustStarted = true; 1104 } 1105 /* 1106 * Not yet, so still buffering audio data. 1107 */ 1108 else 1109 { 1110 LogRel2(("Audio: @%#RX64: Stream '%s' is buffering (%RU8%% complete)...\n", pStreamEx->offInternal, 1111 pStreamEx->Core.szName, (100 * pStreamEx->Out.cbPreBuffered) / pStreamEx->Out.cbPreBufThreshold)); 1112 Assert(cbBuf == 0); 1113 *pcbWritten = cbWritten; 1114 return VINF_SUCCESS; 1115 } 1116 1117 /* 1118 * Write the pre-buffered chunk. 1119 */ 1120 uint32_t off = 0; 1121 uint32_t cbPreBufWritten; 1122 do 1123 { 1124 cbPreBufWritten = 0; 1125 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, &pStreamEx->Out.pbPreBuf[off], 1126 pStreamEx->Out.cbPreBuffered - off, &cbPreBufWritten); 1127 AssertRCBreak(rc); 1128 off += cbPreBufWritten; 1129 } while (off < pStreamEx->Out.cbPreBuffered && cbPreBufWritten != 0); 1130 1131 if (off >= pStreamEx->Out.cbPreBuffered) 1132 { 1133 Assert(off == pStreamEx->Out.cbPreBuffered); 1134 LogFunc(("@%#RX64: Wrote all %#x bytes of pre-buffered audio data.\n", pStreamEx->offInternal, off)); 1135 pStreamEx->Out.cbPreBuffered = 0; 1136 } 1137 else 1138 { 1139 LogRel2(("Audio: @%#RX64: Stream '%s' pre-buffering commit problem: wrote %#x out of %#x + %#x%s - rc=%Rrc *pcbWritten=%#x\n", 1140 pStreamEx->offInternal, pStreamEx->Core.szName, off, pStreamEx->Out.cbPreBuffered, cbBuf, 1141 fJustStarted ? " (just started)" : "", rc, cbWritten)); 1142 AssertMsg(!fJustStarted || RT_FAILURE(rc), 1143 ("Buggy host driver buffer reporting: off=%#x cbPreBuffered=%#x\n", off, pStreamEx->Out.cbPreBuffered)); 1144 if (off > 0) 1145 { 1146 memmove(pStreamEx->Out.pbPreBuf, &pStreamEx->Out.pbPreBuf[off], pStreamEx->Out.cbPreBuffered - off); 1147 pStreamEx->Out.cbPreBuffered -= off; 1148 } 1149 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS(); 1150 *pcbWritten = cbWritten; 1151 return cbWritten ? VINF_SUCCESS : rc; 1152 } 1153 1154 if (RT_FAILURE(rc)) 1155 { 1156 *pcbWritten = cbWritten; 1157 return rc; 1158 } 1159 } 1160 1161 /* 1162 * Do the writing. 1163 */ 1164 uint32_t cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend); 1165 pStreamEx->Out.Stats.cbBackendWritableBefore = cbWritable; 1166 1167 uint8_t const cbFrame = PDMAudioPropsFrameSize(&pStreamEx->Core.Props); 1168 while (cbBuf >= cbFrame && cbWritable >= cbFrame) 1169 { 1170 uint32_t const cbToWrite = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Props, RT_MIN(cbBuf, cbWritable)); 1171 uint32_t cbWrittenNow = 0; 1172 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, pbBuf, cbToWrite, &cbWrittenNow); 1173 if (RT_SUCCESS(rc)) 1174 { 1175 if (cbWrittenNow != cbToWrite) 1176 Log3Func(("%s: @%#RX64: Wrote less bytes than requested: %#x, requested %#x\n", 1177 pStreamEx->Core.szName, pStreamEx->offInternal, cbWrittenNow, cbToWrite)); 1178 #ifdef DEBUG_bird 1179 Assert(cbWrittenNow == cbToWrite); 1180 #endif 1181 AssertStmt(cbWrittenNow <= cbToWrite, cbWrittenNow = cbToWrite); 1182 cbWritten += cbWrittenNow; 1183 cbBuf -= cbWrittenNow; 1184 pbBuf += cbWrittenNow; 1185 pStreamEx->offInternal += cbWrittenNow; 1186 } 1187 else 1188 { 1189 *pcbWritten = cbWritten; 1190 LogFunc(("%s: @%#RX64: pfnStreamPlay failed writing %#x bytes (%#x previous written, %#x writable): %Rrc\n", 1191 pStreamEx->Core.szName, pStreamEx->offInternal, cbToWrite, cbWritten, cbWritable, rc)); 1192 return cbWritten ? VINF_SUCCESS : rc; 1193 } 1194 cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend); 1195 } 1196 1197 *pcbWritten = cbWritten; 1198 pStreamEx->Out.Stats.cbBackendWritableAfter = cbWritable; 1199 if (cbWritten) 1200 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS(); 1201 1202 Log3Func(("%s: @%#RX64: Wrote %#x bytes (%#x bytes left)\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbWritten, cbBuf)); 1203 return rc; 1204 } 1205 1006 1206 1007 1207 /** … … 1022 1222 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 1023 1223 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 1024 AssertPtrNullReturn(pcbWritten, VERR_INVALID_PARAMETER); 1224 uint32_t uTmp; 1225 if (!pcbWritten) 1226 pcbWritten = &uTmp; 1227 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER); 1025 1228 1026 1229 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); … … 1051 1254 !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet")); 1052 1255 *pcbWritten = cbBuf; 1256 pStreamEx->offInternal += cbBuf; 1053 1257 } 1054 1258 /* … … 1058 1262 else if (pStreamEx->fNoMixBufs) 1059 1263 { 1060 rc = VERR_NOT_IMPLEMENTED; 1264 uint64_t offInternalBefore = pStreamEx->offInternal; RT_NOREF(offInternalBefore); 1265 rc = drvAudioStreamWriteNoMixBufs(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten); 1266 Assert(offInternalBefore + *pcbWritten == pStreamEx->offInternal); 1267 if (!pThis->Out.Cfg.Dbg.fEnabled || RT_FAILURE(rc)) 1268 { /* likely */ } 1269 else 1270 AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pvBuf, *pcbWritten, 0 /* fFlags */); 1061 1271 } 1062 1272 /* … … 1139 1349 else 1140 1350 rc = VERR_BUFFER_OVERFLOW; 1141 if (pcbWritten)1142 *pcbWritten= cbWrittenTotal;1351 *pcbWritten = cbWrittenTotal; 1352 pStreamEx->offInternal += cbWrittenTotal; 1143 1353 } 1144 1354 } … … 1319 1529 } 1320 1530 1321 /* Whether to try closing a pending to close stream. */1322 bool fTryClosePending = false;1323 1531 /* 1532 * Pending disable is really what we're here for. This only happens to output streams. 1533 */ 1324 1534 int rc = VINF_SUCCESS; 1325 1326 do 1327 { 1328 if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT) 1329 { 1330 /* No audio frames to transfer from guest to host (anymore)? 1331 * Then try closing this stream if marked so in the next block. */ 1332 uint32_t cFramesLive = AudioMixBufLive(&pStreamEx->Host.MixBuf); 1333 if (cFramesLive && fWorkMixBuf) 1535 if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)) 1536 { /* likely until we get to the end of the stream at least. */ } 1537 else 1538 { 1539 AssertReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT, VINF_SUCCESS); 1540 /** @todo Add a timeout to these proceedings. A few that of the reported buffer 1541 * size. */ 1542 1543 /* 1544 * Check if we have any data we need to write to the backend, try 1545 * move it now. 1546 */ 1547 /** @todo r=bird: It is possible the device has data buffered (e.g. 1548 * internal DMA buffer (HDA) or mixing buffer (HDA + AC'97). We're 1549 * not taking that into account here. I also suspect that neither is 1550 * the device/mixer code, and that if the guest is too quick disabling 1551 * the stream, it will just remain there till the next time something 1552 * is played. That means that this code and associated timer hack 1553 * should probably not be here at all. */ 1554 uint32_t cFramesLive; 1555 if (pStreamEx->fNoMixBufs) 1556 { 1557 cFramesLive = pStreamEx->Out.cbPreBuffered; 1558 if (cFramesLive > 0) 1559 { 1560 uint32_t cbIgnored = 0; 1561 drvAudioStreamWriteNoMixBufs(pThis, pStreamEx, NULL, 0, &cbIgnored); 1562 cFramesLive = PDMAudioPropsBytesToFrames(&pStreamEx->Core.Props, pStreamEx->Out.cbPreBuffered); 1563 } 1564 } 1565 else 1566 { 1567 cFramesLive = AudioMixBufLive(&pStreamEx->Host.MixBuf); 1568 if (cFramesLive > 0 && fWorkMixBuf) 1334 1569 { 1335 1570 uint32_t cIgnored = 0; … … 1338 1573 cFramesLive = AudioMixBufLive(&pStreamEx->Host.MixBuf); 1339 1574 } 1340 1341 fTryClosePending = cFramesLive == 0; 1342 Log3Func(("[%s] fTryClosePending=%RTbool, cFramesLive=%RU32\n", pStreamEx->Core.szName, fTryClosePending, cFramesLive)); 1343 } 1344 1345 /* Has the host stream marked as pending to disable? 1346 * Try disabling the stream then. */ 1347 if ( pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE 1348 && fTryClosePending) 1349 { 1350 /* Tell the backend to drain the stream, that is, play the remaining (buffered) data 1351 * on the backend side. */ 1575 } 1576 Log3Func(("[%s] cFramesLive=%RU32\n", pStreamEx->Core.szName, cFramesLive)); 1577 if (cFramesLive == 0) 1578 { 1579 /* 1580 * Tell the backend to start draining the stream, that is, 1581 * play the remaining buffered data and stop. 1582 */ 1352 1583 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DRAIN); 1353 if (rc == VERR_NOT_SUPPORTED) /* Not all backends support draining . */1584 if (rc == VERR_NOT_SUPPORTED) /* Not all backends support draining yet. */ 1354 1585 rc = VINF_SUCCESS; 1355 1586 if (RT_SUCCESS(rc)) 1356 1587 { 1357 if (pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional to implement. */ 1588 /* 1589 * Before we disable the stream, check if the backend has 1590 * finished playing the buffered data. 1591 */ 1592 uint32_t cbPending; 1593 if (!pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional. */ 1594 cbPending = 0; 1595 else 1358 1596 { 1359 const uint32_t cxPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pStreamEx->pBackend); 1360 Log3Func(("[%s] cxPending=%RU32\n", pStreamEx->Core.szName, cxPending)); 1361 1362 /* Only try close pending if no audio data is pending on the backend-side anymore. */ 1363 fTryClosePending = cxPending == 0; 1597 cbPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pStreamEx->pBackend); 1598 Log3Func(("[%s] cbPending=%RU32 (%#RX32)\n", pStreamEx->Core.szName, cbPending, cbPending)); 1364 1599 } 1365 1366 if (fTryClosePending) 1600 if (cbPending == 0) 1367 1601 { 1602 /* 1603 * Okay, disable it. 1604 */ 1368 1605 LogFunc(("[%s] Closing pending stream\n", pStreamEx->Core.szName)); 1369 1606 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE); … … 1371 1608 { 1372 1609 pStreamEx->Core.fStatus &= ~(PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE); 1373 drvAudioStreamDropInternal(pThis, pStreamEx); 1610 drvAudioStreamDropInternal(pThis, pStreamEx); /* Not a DROP command, just a stream reset. */ 1374 1611 } 1375 1612 else 1376 LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pStreamEx->Core.szName, rc));1613 LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pStreamEx->Core.szName, rc)); 1377 1614 } 1378 1615 } 1379 1616 } 1380 1617 1381 } while (0);1618 } 1382 1619 1383 1620 /* Update timestamps. */ … … 2358 2595 if (pCfgReq->Backend.cFramesPreBuffering == UINT32_MAX) /* Set default pre-buffering size if nothing explicitly is set. */ 2359 2596 { 2360 /* For pre-buffering to finish the buffer at least must be full one time. */2361 pCfgReq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesBufferSize ;2597 /* Pre-buffer 66% of the buffer. */ 2598 pCfgReq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesBufferSize * 2 / 3; 2362 2599 pszWhat = "default"; 2363 2600 } … … 2523 2760 Assert(PDMAudioPropsAreValid(&CfgHostAcq.Props)); 2524 2761 2762 /* Set the stream properties (currently guest side, when DevSB16 is 2763 converted to mixer and PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF becomes 2764 default, this will just be the stream properties). */ 2765 if (fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF) 2766 pStreamEx->Core.Props = CfgHostAcq.Props; 2767 else 2768 pStreamEx->Core.Props = pCfgGuest->Props; 2769 2525 2770 /* Let the user know if the backend changed some of the tweakable values. */ 2526 2771 if (CfgHostAcq.Backend.cFramesBufferSize != pCfgHost->Backend.cFramesBufferSize) … … 2540 2785 2541 2786 /* 2542 * Configure host buffers. 2543 */ 2544 2545 /* Check if the backend did return sane values and correct if necessary. 2546 * Should never happen with our own backends, but you never know ... */ 2547 if (CfgHostAcq.Backend.cFramesBufferSize < CfgHostAcq.Backend.cFramesPreBuffering) 2548 { 2549 LogRel2(("Audio: Warning: Pre-buffering size (%RU32 frames) of stream '%s' does not match buffer size (%RU32 frames), " 2550 "setting pre-buffering size to %RU32 frames\n", 2551 CfgHostAcq.Backend.cFramesPreBuffering, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, CfgHostAcq.Backend.cFramesBufferSize)); 2552 CfgHostAcq.Backend.cFramesPreBuffering = CfgHostAcq.Backend.cFramesBufferSize; 2787 * Check if the backend did return sane values and correct if necessary. 2788 * Should never happen with our own backends, but you never know ... 2789 */ 2790 uint32_t const cFramesPreBufferingMax = CfgHostAcq.Backend.cFramesBufferSize - RT_MIN(16, CfgHostAcq.Backend.cFramesBufferSize); 2791 if (CfgHostAcq.Backend.cFramesPreBuffering > cFramesPreBufferingMax) 2792 { 2793 LogRel2(("Audio: Warning: Pre-buffering size of %RU32 frames for stream '%s' is too close to or larger than the %RU32 frames buffer size, reducing it to %RU32 frames!\n", 2794 CfgHostAcq.Backend.cFramesPreBuffering, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, cFramesPreBufferingMax)); 2795 AssertFailed(); 2796 CfgHostAcq.Backend.cFramesPreBuffering = cFramesPreBufferingMax; 2553 2797 } 2554 2798 2555 2799 if (CfgHostAcq.Backend.cFramesPeriod > CfgHostAcq.Backend.cFramesBufferSize) 2556 2800 { 2557 LogRel2(("Audio: Warning: Period size (%RU32 frames) of stream '%s' does not match buffer size (%RU32 frames), setting to %RU32 frames\n", 2558 CfgHostAcq.Backend.cFramesPeriod, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, CfgHostAcq.Backend.cFramesBufferSize)); 2559 CfgHostAcq.Backend.cFramesPeriod = CfgHostAcq.Backend.cFramesBufferSize; 2560 } 2561 2562 uint64_t msBufferSize = PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize); 2563 LogRel2(("Audio: Buffer size of stream '%s' is %RU64ms (%RU32 frames)\n", 2564 pStreamEx->Core.szName, msBufferSize, CfgHostAcq.Backend.cFramesBufferSize)); 2565 2566 /* If no own pre-buffer is set, let the backend choose. */ 2567 uint64_t msPreBuf = PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering); 2568 LogRel2(("Audio: Pre-buffering size of stream '%s' is %RU64ms (%RU32 frames)\n", 2569 pStreamEx->Core.szName, msPreBuf, CfgHostAcq.Backend.cFramesPreBuffering)); 2801 LogRel2(("Audio: Warning: Period size of %RU32 frames for stream '%s' is larger than the %RU32 frames buffer size, reducing it to %RU32 frames!\n", 2802 CfgHostAcq.Backend.cFramesPeriod, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, CfgHostAcq.Backend.cFramesBufferSize / 2)); 2803 AssertFailed(); 2804 CfgHostAcq.Backend.cFramesPeriod = CfgHostAcq.Backend.cFramesBufferSize / 2; 2805 } 2806 2807 LogRel2(("Audio: Buffer size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName, 2808 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize), CfgHostAcq.Backend.cFramesBufferSize)); 2809 LogRel2(("Audio: Pre-buffering size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName, 2810 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering), CfgHostAcq.Backend.cFramesPreBuffering)); 2570 2811 2571 2812 /* Make sure the configured buffer size by the backend at least can hold the configured latency. */ 2572 2813 const uint32_t msPeriod = PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPeriod); 2573 2574 LogRel2(("Audio: Period size of stream '%s' is %RU64ms (%RU32 frames)\n", 2814 LogRel2(("Audio: Period size of stream '%s' is %RU64 ms / %RU32 frames\n", 2575 2815 pStreamEx->Core.szName, msPeriod, CfgHostAcq.Backend.cFramesPeriod)); 2576 2816 2577 2817 if ( pCfgGuest->Device.cMsSchedulingHint /* Any scheduling hint set? */ 2578 2818 && pCfgGuest->Device.cMsSchedulingHint > msPeriod) /* This might lead to buffer underflows. */ 2579 {2580 2819 LogRel(("Audio: Warning: Scheduling hint of stream '%s' is bigger (%RU64ms) than used period size (%RU64ms)\n", 2581 2820 pStreamEx->Core.szName, pCfgGuest->Device.cMsSchedulingHint, msPeriod)); 2582 } 2821 2822 /* 2823 * Make a copy of the acquired host stream configuration and the guest side one. 2824 */ 2825 rc = PDMAudioStrmCfgCopy(&pStreamEx->Host.Cfg, &CfgHostAcq); 2826 AssertRC(rc); 2827 2828 /* Set the guests's default audio data layout. */ 2829 pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; /** @todo r=bird: WTF DO WE DO THIS? It's input and probably should've been const... */ 2830 rc = PDMAudioStrmCfgCopy(&pStreamEx->Guest.Cfg, pCfgGuest); 2831 AssertRC(rc); 2832 2833 /* 2834 * Configure host buffers. 2835 */ 2583 2836 2584 2837 /* Destroy any former mixing buffer. */ … … 2590 2843 AssertRCReturn(rc, rc); 2591 2844 } 2592 2593 /* Make a copy of the acquired host stream configuration. */ 2594 rc = PDMAudioStrmCfgCopy(&pStreamEx->Host.Cfg, &CfgHostAcq); 2595 AssertRC(rc); 2845 /* Allocate space for pre-buffering of output stream w/o mixing buffers. */ 2846 else if (pCfgHost->enmDir == PDMAUDIODIR_OUT) 2847 { 2848 Assert(pStreamEx->Out.cbPreBufAlloc == 0); 2849 Assert(pStreamEx->Out.cbPreBufThreshold == 0); 2850 Assert(pStreamEx->Out.cbPreBuffered == 0); 2851 if (CfgHostAcq.Backend.cFramesPreBuffering != 0) 2852 { 2853 pStreamEx->Out.cbPreBufThreshold = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering); 2854 pStreamEx->Out.cbPreBufAlloc = RT_ALIGN_32(pStreamEx->Out.cbPreBufThreshold + _8K, _4K); 2855 pStreamEx->Out.pbPreBuf = (uint8_t *)RTMemAllocZ(pStreamEx->Out.cbPreBufAlloc); 2856 AssertReturn(pStreamEx->Out.pbPreBuf, VERR_NO_MEMORY); 2857 } 2858 } 2596 2859 2597 2860 /* 2598 2861 * Init guest stream. 2599 2862 */ 2600 2601 2863 if (pCfgGuest->Device.cMsSchedulingHint) 2602 2864 LogRel2(("Audio: Stream '%s' got a scheduling hint of %RU32ms (%RU32 bytes)\n", … … 2607 2869 AudioMixBufDestroy(&pStreamEx->Guest.MixBuf); 2608 2870 2609 /* Set the guests's default audio data layout. */2610 pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;2611 2612 2871 if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF)) 2613 2872 { … … 2615 2874 AssertRCReturn(rc, rc); 2616 2875 } 2617 2618 /* Make a copy of the guest stream configuration. */2619 rc = PDMAudioStrmCfgCopy(&pStreamEx->Guest.Cfg, pCfgGuest);2620 AssertRC(rc);2621 2876 2622 2877 if (RT_FAILURE(rc)) … … 2732 2987 * Assert sanity. 2733 2988 */ 2734 AssertReturn(!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF), VERR_INVALID_FLAGS);2989 AssertReturn(!(fFlags & ~PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF), VERR_INVALID_FLAGS); 2735 2990 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER); 2736 2991 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER); … … 3099 3354 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, 0); 3100 3355 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, 0); 3101 AssertMsg (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));3356 AssertMsgReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"), 0); 3102 3357 int rc = RTCritSectEnter(&pThis->CritSect); 3103 3358 AssertRCReturn(rc, 0); … … 3108 3363 uint32_t cbWritable = 0; 3109 3364 3110 /* Note: We don't propag e the backend stream's status to the outside -- it's the job of this3365 /* Note: We don't propagate the backend stream's status to the outside -- it's the job of this 3111 3366 * audio connector to make sense of it. */ 3112 3367 if (PDMAudioStrmStatusCanWrite(pStreamEx->Core.fStatus)) 3113 3368 { 3114 3369 if (pStreamEx->fNoMixBufs) 3370 { 3371 Assert(pThis->pHostDrvAudio); 3115 3372 cbWritable = pThis->pHostDrvAudio 3116 3373 ? pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend) : 0; 3374 if (pStreamEx->fThresholdReached) 3375 { 3376 if (pStreamEx->Out.cbPreBuffered == 0) 3377 { /* likely */ } 3378 else 3379 { 3380 /* Buggy backend: We weren't able to copy all the pre-buffered data to it 3381 when reaching the threshold. Try escape this situation, or at least 3382 keep the extra buffering to a minimum. We must try write something 3383 as long as there is space for it, as we need the pfnStreamWrite call 3384 to move the data. */ 3385 uint32_t const cbMin = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Props, 8); 3386 if (cbWritable >= pStreamEx->Out.cbPreBuffered + cbMin) 3387 cbWritable -= pStreamEx->Out.cbPreBuffered + cbMin / 2; 3388 else 3389 cbWritable = RT_MIN(cbMin, pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered); 3390 AssertLogRel(cbWritable); 3391 } 3392 } 3393 else 3394 { 3395 Assert(cbWritable >= pStreamEx->Out.cbPreBufThreshold); 3396 cbWritable = pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBufThreshold; 3397 } 3398 } 3117 3399 else 3118 3400 cbWritable = AudioMixBufFreeBytes(&pStreamEx->Host.MixBuf); … … 3172 3454 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 3173 3455 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 3456 AssertReturn(pStreamEx->fNoMixBufs, VWRN_INVALID_STATE); 3174 3457 3175 3458 LogFlowFunc(("[%s] volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStreamEx->Core.szName, pVol->uLeft, pVol->uRight, pVol->fMuted)); 3176 AssertReturn(!pStreamEx->fNoMixBufs, VERR_ACCESS_DENIED);3177 3459 3178 3460 AudioMixBufSetVolume(&pStreamEx->Guest.MixBuf, pVol); … … 3248 3530 AudioMixBufDestroy(&pStreamEx->Guest.MixBuf); 3249 3531 AudioMixBufDestroy(&pStreamEx->Host.MixBuf); 3532 3533 /* Free pre-buffer space. */ 3534 if ( pStreamEx->Core.enmDir == PDMAUDIODIR_OUT 3535 && pStreamEx->Out.pbPreBuf) 3536 { 3537 RTMemFree(pStreamEx->Out.pbPreBuf); 3538 pStreamEx->Out.pbPreBuf = NULL; 3539 pStreamEx->Out.cbPreBufAlloc = 0; 3540 pStreamEx->Out.cbPreBuffered = 0; 3541 } 3250 3542 3251 3543 if (RT_SUCCESS(rc)) -
trunk/src/VBox/Devices/Audio/testcase/tstAudioMixBuffer.cpp
r88356 r88433 548 548 549 549 550 static void tstNewPeek(RTTEST hTest, uint32_t uFromHz, uint32_t uToHz) 551 { 552 RTTestSubF(hTest, "New peek %u to %u Hz (S16)", uFromHz, uToHz); 553 554 struct { int16_t l, r; } 555 aSrcFrames[4096], 556 aDstFrames[4096]; 557 558 /* Mix buffer is uFromHz 2ch S16 */ 559 uint32_t const cFrames = RTRandU32Ex(16, RT_ELEMENTS(aSrcFrames)); 560 PDMAUDIOPCMPROPS const CfgSrc = PDMAUDIOPCMPROPS_INITIALIZER(2 /*cbSample*/, true /*fSigned*/, 2 /*ch*/, uFromHz, false /*fSwap*/); 561 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&CfgSrc)); 562 AUDIOMIXBUF MixBuf; 563 RTTESTI_CHECK_RC_OK_RETV(AudioMixBufInit(&MixBuf, "NewPeekMixBuf", &CfgSrc, cFrames)); 564 565 /* Peek state (destination) is uToHz 2ch S16 */ 566 PDMAUDIOPCMPROPS const CfgDst = PDMAUDIOPCMPROPS_INITIALIZER(2 /*cbSample*/, true /*fSigned*/, 2 /*ch*/, uToHz, false /*fSwap*/); 567 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&CfgDst)); 568 AUDIOMIXBUFPEEKSTATE PeekState; 569 RTTESTI_CHECK_RC_OK_RETV(AudioMixBufInitPeekState(&MixBuf, &PeekState, &CfgDst)); 570 571 /* 572 * Test parameters. 573 */ 574 uint32_t const cMaxSrcFrames = RT_MIN(cFrames * uFromHz / uToHz - 1, cFrames); 575 uint32_t const cIterations = RTRandU32Ex(64, 1024); 576 RTTestErrContext(hTest, "cFrames=%RU32 cMaxSrcFrames=%RU32 cIterations=%RU32", cFrames, cMaxSrcFrames, cIterations); 577 RTTestPrintf(hTest, RTTESTLVL_DEBUG, "cFrames=%RU32 cMaxSrcFrames=%RU32 cIterations=%RU32\n", 578 cFrames, cMaxSrcFrames, cIterations); 579 580 /* 581 * We generate a simple "A" sine wave as input. 582 */ 583 uint32_t iSrcFrame = 0; 584 uint32_t iDstFrame = 0; 585 double rdFixed = 2.0 * M_PI * 440.0 /* A */ / PDMAudioPropsHz(&CfgSrc); /* Fixed sin() input. */ 586 for (uint32_t i = 0; i < cIterations; i++) 587 { 588 RTTestPrintf(hTest, RTTESTLVL_DEBUG, "i=%RU32\n", i); 589 590 /* 591 * Generate source frames and write them. 592 */ 593 uint32_t const cSrcFrames = i < cIterations / 2 594 ? RTRandU32Ex(2, cMaxSrcFrames) & ~(uint32_t)1 595 : RTRandU32Ex(1, cMaxSrcFrames - 1) | 1; 596 for (uint32_t j = 0; j < cSrcFrames; j++, iSrcFrame++) 597 aSrcFrames[j].r = aSrcFrames[j].l = 32760 /*Amplitude*/ * sin(rdFixed * iSrcFrame); 598 599 uint32_t cSrcFramesWritten = UINT32_MAX / 2; 600 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufWriteCirc(&MixBuf, &aSrcFrames, cSrcFrames * sizeof(aSrcFrames[0]), &cSrcFramesWritten)); 601 RTTESTI_CHECK_MSG_BREAK(cSrcFrames == cSrcFramesWritten, 602 ("cSrcFrames=%RU32 vs cSrcFramesWritten=%RU32 cLiveFrames=%RU32\n", 603 cSrcFrames, cSrcFramesWritten, AudioMixBufLive(&MixBuf))); 604 605 /* 606 * Read out all the frames using the peek function. 607 */ 608 uint32_t offSrcFrame = 0; 609 while (offSrcFrame < cSrcFramesWritten) 610 { 611 uint32_t cSrcFramesToRead = cSrcFramesWritten - offSrcFrame; 612 uint32_t cTmp = (uint64_t)cSrcFramesToRead * uToHz / uFromHz; 613 if (cTmp + 32 >= RT_ELEMENTS(aDstFrames)) 614 cSrcFramesToRead = ((uint64_t)RT_ELEMENTS(aDstFrames) - 32) * uFromHz / uToHz; /* kludge */ 615 616 uint32_t cSrcFramesPeeked = UINT32_MAX / 4; 617 uint32_t cbDstPeeked = UINT32_MAX / 2; 618 RTRandBytes(aDstFrames, sizeof(aDstFrames)); 619 AudioMixBufPeek(&MixBuf, offSrcFrame, cSrcFramesToRead, &cSrcFramesPeeked, 620 &PeekState, aDstFrames, sizeof(aDstFrames), &cbDstPeeked); 621 uint32_t cDstFramesPeeked = PDMAudioPropsBytesToFrames(&CfgDst, cbDstPeeked); 622 RTTESTI_CHECK(cbDstPeeked > 0 || cSrcFramesPeeked > 0); 623 624 if (uFromHz == uToHz) 625 { 626 for (uint32_t iDst = 0; iDst < cDstFramesPeeked; iDst++) 627 if (memcmp(&aDstFrames[iDst], &aSrcFrames[offSrcFrame + iDst], sizeof(aSrcFrames[0])) != 0) 628 RTTestFailed(hTest, "Frame #%u differs: %#x / %#x, expected %#x / %#x\n", iDstFrame + iDst, 629 aDstFrames[iDst].l, aDstFrames[iDst].r, 630 aSrcFrames[iDst + offSrcFrame].l, aSrcFrames[iDst + offSrcFrame].r); 631 } 632 633 offSrcFrame += cSrcFramesPeeked; 634 iDstFrame += cDstFramesPeeked; 635 } 636 637 /* 638 * Then advance. 639 */ 640 AudioMixBufAdvance(&MixBuf, cSrcFrames); 641 RTTESTI_CHECK(AudioMixBufLive(&MixBuf) == 0); 642 } 643 644 /** @todo this is a bit lax... */ 645 uint32_t const cDstMinExpect = ((uint64_t)iSrcFrame * uToHz - uFromHz - 1) / uFromHz; 646 uint32_t const cDstMaxExpect = ((uint64_t)iSrcFrame * uToHz + uFromHz - 1) / uFromHz; 647 RTTESTI_CHECK_MSG(iDstFrame >= cDstMinExpect && iDstFrame <= cDstMaxExpect, 648 ("iSrcFrame=%#x -> %#x..%#x; iDstFrame=%#x (delta %d)\n", 649 iSrcFrame, cDstMinExpect, cDstMaxExpect, iDstFrame, (cDstMinExpect + cDstMaxExpect) / 2 - iDstFrame)); 650 651 AudioMixBufDestroy(&MixBuf); 652 } 653 654 550 655 /* Test 8-bit sample conversion (8-bit -> internal -> 8-bit). */ 551 656 static int tstConversion8(RTTEST hTest) … … 883 988 tstDownsampling(hTest, 48000, 22050); 884 989 tstDownsampling(hTest, 48000, 11000); 990 tstNewPeek(hTest, 48000, 48000); 991 tstNewPeek(hTest, 48000, 11000); 992 tstNewPeek(hTest, 48000, 44100); 993 tstNewPeek(hTest, 44100, 22050); 994 tstNewPeek(hTest, 44100, 11000); 995 //tstNewPeek(hTest, 11000, 48000); 996 //tstNewPeek(hTest, 22050, 44100); 885 997 886 998 /*
Note:
See TracChangeset
for help on using the changeset viewer.