VirtualBox

Changeset 88433 in vbox for trunk/src/VBox/Devices


Ignore:
Timestamp:
Apr 9, 2021 12:55:19 PM (4 years ago)
Author:
vboxsync
Message:

Audio: Eliminated the DrvAudio mixing buffers for output streams on devices without their own mixer. Changed the prebuffering to not default to the whole backend buffer size, but only 2/3 of it, so that there is room for a bit of incoming data from the device once we start playing. We don't want to have that gather in the device mixer or internal DMA buffers. This code needs some more testing and work, only tested on linux against ALSA. bugref:9890

Location:
trunk/src/VBox/Devices/Audio
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/AudioMixBuffer.cpp

    r88356 r88433  
    400400 * Note: Currently does not handle any endianness conversion yet!
    401401 */
    402 #define AUDMIXBUF_CONVERT(_aName, _aType, _aMin, _aMax, _aSigned, _aShift) \
     402#define AUDMIXBUF_CONVERT(a_Name, a_Type, _aMin, _aMax, _aSigned, _aShift) \
    403403    /* 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) \
    405405    { \
    406406        /* left shifting of signed values is not defined, therefore the intermediate uint64_t cast */ \
     
    411411    \
    412412    /* 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) \
    414414    { \
    415415        /*if (iVal >= 0x7fffffff) return _aMax; if (iVal < -INT64_C(0x80000000)) return _aMin;*/ \
     
    417417        { \
    418418            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)); \
    421421        } \
    422422        return iVal >= 0 ? _aMax : _aMin; \
    423423    } \
    424424    \
    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, \
    426426                                                               PCAUDMIXBUFCONVOPTS pOpts) \
    427427    { \
    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)); \
    430430        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)); \
    432432        for (uint32_t i = 0; i < cFrames; i++) \
    433433        { \
    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; \
    436436            paDst++; \
    437437        } \
     
    440440    } \
    441441    \
    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, \
    443443                                                             PCAUDMIXBUFCONVOPTS pOpts) \
    444444    { \
    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)); \
    447447        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)); \
    449449        for (uint32_t i = 0; i < cFrames; i++) \
    450450        { \
    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; \
    453453            pSrc++; \
    454454            paDst++; \
     
    458458    } \
    459459    \
    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) \
    461461    { \
    462462        PCPDMAUDIOFRAME pSrc = paSrc; \
    463         _aType *pDst = (_aType *)pvDst; \
     463        a_Type *pDst = (a_Type *)pvDst; \
    464464        uint32_t cFrames = pOpts->cFrames; \
    465465        while (cFrames--) \
    466466        { \
    467467            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); \
    470470            AUDMIXBUF_MACRO_LOG(("\t-> l=%RI16, r=%RI16\n", pDst[0], pDst[1])); \
    471471            pDst += 2; \
     
    474474    } \
    475475    \
    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) \
    477477    { \
    478478        PCPDMAUDIOFRAME pSrc = paSrc; \
    479         _aType *pDst = (_aType *)pvDst; \
     479        a_Type *pDst = (a_Type *)pvDst; \
    480480        uint32_t cFrames = pOpts->cFrames; \
    481481        while (cFrames--) \
    482482        { \
    483             *pDst++ = audioMixBufClipTo##_aName((pSrc->i64LSample + pSrc->i64RSample) / 2); \
     483            *pDst++ = audioMixBufClipTo##a_Name((pSrc->i64LSample + pSrc->i64RSample) / 2); \
    484484            pSrc++; \
    485485        } \
    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
    487547
    488548/* audioMixBufConvXXXS8: 8 bit, signed. */
     
    529589}
    530590
     591
     592static DECLCALLBACK(void)
     593audioMixBufEncode2ChTo2ChRaw(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
     599static DECLCALLBACK(void)
     600audioMixBufEncode2ChTo1ChRaw(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
     612static DECLCALLBACK(void)
     613audioMixBufEncode1ChTo2ChRaw(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
     625static DECLCALLBACK(void)
     626audioMixBufEncode1ChTo1ChRaw(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}
    531639
    532640
     
    564672        PPDMAUDIOFRAME pDst      = paDst; \
    565673        PPDMAUDIOFRAME pDstEnd   = &paDst[cDstFrames]; \
    566         PDMAUDIOFRAME  frameLast = pRate->SrcFrameLast; \
     674        PDMAUDIOFRAME  frameLast = pRate->SrcLast.Frame; \
    567675        \
    568676        while ((uintptr_t)pDst < (uintptr_t)pDstEnd) \
     
    608716        } \
    609717        \
    610         pRate->SrcFrameLast = frameLast; \
     718        pRate->SrcLast.Frame = frameLast; \
    611719        if (pcDstWritten) \
    612720            *pcDstWritten = pDst - paDst; \
     
    12531361
    12541362#ifdef DEBUG
     1363
    12551364/**
    12561365 * Prints a single mixing buffer.
     
    13921501    audioMixBufDbgPrintInternal(pMixBuf, __FUNCTION__);
    13931502}
     1503
    13941504#endif /* DEBUG */
    13951505
     
    16541764}
    16551765
     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 */
     1774void 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
     1863AUDIOMIXBUF_RESAMPLE(1,Generic)
     1864AUDIOMIXBUF_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 */
     1872int 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 */
     2027DECL_NO_INLINE(static, void)
     2028AudioMixBufPeekResampling(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 */
     2074void 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 */
     2131void 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
    16562143/**
    16572144 * Sets the overall (master) volume.
     
    19132400                           const void *pvBuf, uint32_t cbBuf, uint32_t *pcWritten)
    19142401{
     2402    /*
     2403     * Assert sanity.
     2404     */
    19152405    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);
    19172410    AssertPtrNullReturn(pcWritten, VERR_INVALID_POINTER);
    19182411
    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
    19202419    {
    19212420        if (pcWritten)
    19222421            *pcWritten = 0;
     2422        AssertReturn(cbBuf == 0, VERR_INVALID_PARAMETER);
    19232423        return VINF_SUCCESS;
    19242424    }
    19252425
    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     */
    19322430    PFNAUDIOMIXBUFCONVFROM pfnConvFrom = NULL;
    19332431    if (!pMixBuf->Volume.fMuted)
     
    19422440        pfnConvFrom = &audioMixBufConvFromSilence;
    19432441
    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         */
    19672505#ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
    19682506        RTFILE fh;
    19692507        RTFileOpen(&fh, AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH "mixbuf_writecirc_ex.pcm",
    19702508                   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);
    19722510        RTFileClose(fh);
    19732511#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);
    19792512    }
    19802513    else
     
    19872520
    19882521    if (pcWritten)
    1989         *pcWritten = cWritten;
     2522        *pcWritten = cFramesWritten;
    19902523
    19912524#ifdef AUDMIXBUF_LOG_ENABLED
    19922525    char szTmp[PDMAUDIOPROPSTOSTRING_MAX];
    19932526#endif
    1994     AUDMIXBUF_LOG(("%s: pSrcProps=%s, cbBuf=%RU32 (%RU32 frames), cWritten=%RU32, rc=%Rrc\n", pMixBuf->pszName,
    1995                    PDMAudioPropsToString(pSrcProps, szTmp, sizeof(szTmp)), cbBuf, AUDIOMIXBUF_B2F(pMixBuf, cbBuf), cWritten, 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));
    19962529    return rc;
    19972530}
  • trunk/src/VBox/Devices/Audio/AudioMixBuffer.h

    r88356 r88433  
    4242    /** Current (absolute) offset in the input stream. */
    4343    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
    4648    /** Last processed frame of the input stream.
    4749     *  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
    4964} AUDIOSTREAMRATE;
    5065/** Pointer to rate processing information of a stream. */
     
    129144/** Pointer to audio mixing buffer.  */
    130145typedef struct AUDIOMIXBUF *PAUDIOMIXBUF;
     146/** Pointer to a const audio mixing buffer.  */
     147typedef struct AUDIOMIXBUF const *PCAUDIOMIXBUF;
     148
     149
     150/**
     151 * State & config for AudioMixBufPeek created by AudioMixBufInitPeekState.
     152 */
     153typedef 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. */
     167typedef AUDIOMIXBUFPEEKSTATE *PAUDIOMIXBUFPEEKSTATE;
     168
    131169
    132170/**
     
    234272int     AudioMixBufInit(PAUDIOMIXBUF pMixBuf, const char *pszName, PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames);
    235273void    AudioMixBufDestroy(PAUDIOMIXBUF pMixBuf);
     274
     275int     AudioMixBufInitPeekState(PCAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFPEEKSTATE pState, PCPDMAUDIOPCMPROPS pDstProps);
     276void    AudioMixBufPeek(PCAUDIOMIXBUF pMixBuf, uint32_t offSrcFrame, uint32_t cMaxSrcFrames, uint32_t *pcSrcFramesPeeked,
     277                        PAUDIOMIXBUFPEEKSTATE pState, void *pvDst, uint32_t cbDst, uint32_t *pcbDstPeeked);
     278void    AudioMixBufAdvance(PAUDIOMIXBUF pMixBuf, uint32_t cFrames);
     279void    AudioMixBufDrop(PAUDIOMIXBUF pMixBuf);
     280
    236281void AudioMixBufClear(PAUDIOMIXBUF pMixBuf);
    237282void AudioMixBufFinish(PAUDIOMIXBUF pMixBuf, uint32_t cFramesToClear);
  • trunk/src/VBox/Devices/Audio/AudioMixer.cpp

    r88374 r88433  
    104104static void audioMixerSinkReset(PAUDMIXSINK pSink);
    105105static 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);
    110106
    111107static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl);
     
    575571    /** @todo Validate fFlags. */
    576572    /* ppStream is optional. */
     573    RT_NOREF(pDevIns); /* we'll probably be adding more statistics */
    577574
    578575    /*
     
    603600        if (RT_SUCCESS(rc))
    604601        {
    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);
    606608            if (RT_SUCCESS(rc))
    607609            {
    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));
    610612
    611613                /*
    612                  * Lock the sink so we can safely get it's properties and call
    613                  * 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.
    614616                 */
    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);
    617650                if (RT_SUCCESS(rc))
    618651                {
    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);
    654655                    if (RT_SUCCESS(rc))
    655656                    {
     
    664665                        RTCritSectLeave(&pSink->CritSect);
    665666
    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 
    674667                        if (ppStream)
    675668                            *ppStream = pMixStream;
     
    677670                    }
    678671
    679                     /*
    680                      * Failed.  Tear down the stream.
    681                      */
    682                     int rc2 = RTCritSectLeave(&pSink->CritSect);
    683                     AssertRC(rc2);
     672                    rc = pConn->pfnStreamDestroy(pConn, pStream);
    684673                }
    685                 RTCircBufDestroy(pMixStream->pCircBuf);
     674
     675                /*
     676                 * Failed.  Tear down the stream.
     677                 */
     678                int rc2 = RTCritSectLeave(&pSink->CritSect);
     679                AssertRC(rc2);
    686680            }
    687681            RTCritSectDelete(&pMixStream->CritSect);
     
    16501644    memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
    16511645
    1652     LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU8, rVol=%RU8\n",
    1653                  pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
    1654 
    16551646    LogRel2(("Audio Mixer: Setting volume of sink '%s' to %RU8/%RU8 (%s)\n",
    16561647             pSink->pszName, pVol->uLeft, pVol->uRight, pVol->fMuted ? "Muted" : "Unmuted"));
     
    16661657
    16671658/**
    1668  * Updates a mixer sink, internal version.
     1659 * Updates an input mixer sink.
    16691660 *
    16701661 * @returns VBox status code.
    16711662 * @param   pSink               Mixer sink to update.
    16721663 */
    1673 static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
     1664static 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 */
     1765static 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 */
     1927int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
    16741928{
    16751929    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     1930    int rc = RTCritSectEnter(&pSink->CritSect);
     1931    AssertRCReturn(rc, rc);
    16761932
    16771933#ifdef LOG_ENABLED
     
    16801936    Log3Func(("[%s] fStatus=%s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
    16811937
    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);
    18491953    return rc;
    18501954}
     
    18801984             pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
    18811985
    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        }
    18881998    }
    18891999
     
    18922002
    18932003/**
    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.
    20772005 *
    20782006 * @returns VBox status code.
     
    21302058}
    21312059
     2060
    21322061/*********************************************************************************************************************************
    21332062 * Mixer Stream implementation.
     
    22582187    pMixStream->pszName = NULL;
    22592188
    2260     if (pMixStream->pCircBuf)
    2261     {
    2262         RTCircBufDestroy(pMixStream->pCircBuf);
    2263         pMixStream->pCircBuf = NULL;
    2264     }
    2265 
    22662189    int rc2 = RTCritSectDelete(&pMixStream->CritSect);
    22672190    AssertRC(rc2);
     
    23782301    return fIsValid;
    23792302}
     2303
  • trunk/src/VBox/Devices/Audio/AudioMixer.h

    r88357 r88433  
    9191    /** The statistics prefix. */
    9292    char                   *pszStatPrefix;
    93     /** The streams's critical section. */
    94     RTCRITSECT              CritSect;
    9593    /** Sink this stream is attached to. */
    9694    PAUDMIXSINK             pSink;
     
    103101    /** Pointer to PDM audio stream this mixer stream handles. */
    104102    PPDMAUDIOSTREAM         pStream;
     103    /** Mixing buffer peeking state & config. */
     104    AUDIOMIXBUFPEEKSTATE    PeekState;
    105105    /** Last read (recording) / written (playback) timestamp (in ns). */
    106106    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;
    114109} AUDMIXSTREAM, *PAUDMIXSTREAM;
    115110
     
    124119#define AUDMIXSINK_STS_PENDING_DISABLE       RT_BIT(1)
    125120/** 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. */
    131125#define AUDMIXSINK_STS_DIRTY                 RT_BIT(2)
    132126
    133127/**
    134128 * Audio mixer sink direction.
     129 * @todo r=bird: use PDMAUDIODIR instead.
    135130 */
    136131typedef enum AUDMIXSINKDIR
  • trunk/src/VBox/Devices/Audio/DrvAudio.cpp

    r88412 r88433  
    128128     *  write (output streams). */
    129129    uint64_t            nsLastReadWritten;
     130    /** Internal stream position (as per pfnStreamWrite/Read). */
     131    uint64_t            offInternal;
    130132
    131133
     
    179181                uint32_t        cbBackendWritableAfter;
    180182            } 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
    181193            /** Hack alert: Max writable amount reported by the backend.
    182194             * This is used to aid buffer underrun detection in DrvAudio while playing.
     
    958970    LogFunc(("[%s]\n", pStreamEx->Core.szName));
    959971
    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    }
    962977
    963978    pStreamEx->fThresholdReached    = false;
     
    10041019#endif
    10051020}
     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 */
     1033static 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
    10061206
    10071207/**
     
    10221222    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    10231223    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);
    10251228
    10261229    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     
    10511254                      !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet"));
    10521255            *pcbWritten = cbBuf;
     1256            pStreamEx->offInternal += cbBuf;
    10531257        }
    10541258        /*
     
    10581262        else if (pStreamEx->fNoMixBufs)
    10591263        {
    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 */);
    10611271        }
    10621272        /*
     
    11391349            else
    11401350                rc = VERR_BUFFER_OVERFLOW;
    1141             if (pcbWritten)
    1142                 *pcbWritten = cbWrittenTotal;
     1351            *pcbWritten = cbWrittenTotal;
     1352            pStreamEx->offInternal += cbWrittenTotal;
    11431353        }
    11441354    }
     
    13191529    }
    13201530
    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     */
    13241534    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)
    13341569            {
    13351570                uint32_t cIgnored = 0;
     
    13381573                cFramesLive = AudioMixBufLive(&pStreamEx->Host.MixBuf);
    13391574            }
    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             */
    13521583            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. */
    13541585                rc = VINF_SUCCESS;
    13551586            if (RT_SUCCESS(rc))
    13561587            {
    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
    13581596                {
    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));
    13641599                }
    1365 
    1366                 if (fTryClosePending)
     1600                if (cbPending == 0)
    13671601                {
     1602                    /*
     1603                     * Okay, disable it.
     1604                     */
    13681605                    LogFunc(("[%s] Closing pending stream\n", pStreamEx->Core.szName));
    13691606                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     
    13711608                    {
    13721609                        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. */
    13741611                    }
    13751612                    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));
    13771614                }
    13781615            }
    13791616        }
    13801617
    1381     } while (0);
     1618    }
    13821619
    13831620    /* Update timestamps. */
     
    23582595        if (pCfgReq->Backend.cFramesPreBuffering == UINT32_MAX) /* Set default pre-buffering size if nothing explicitly is set. */
    23592596        {
    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;
    23622599            pszWhat = "default";
    23632600        }
     
    25232760    Assert(PDMAudioPropsAreValid(&CfgHostAcq.Props));
    25242761
     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
    25252770    /* Let the user know if the backend changed some of the tweakable values. */
    25262771    if (CfgHostAcq.Backend.cFramesBufferSize != pCfgHost->Backend.cFramesBufferSize)
     
    25402785
    25412786    /*
    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;
    25532797    }
    25542798
    25552799    if (CfgHostAcq.Backend.cFramesPeriod > CfgHostAcq.Backend.cFramesBufferSize)
    25562800    {
    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));
    25702811
    25712812    /* Make sure the configured buffer size by the backend at least can hold the configured latency. */
    25722813    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",
    25752815             pStreamEx->Core.szName, msPeriod, CfgHostAcq.Backend.cFramesPeriod));
    25762816
    25772817    if (   pCfgGuest->Device.cMsSchedulingHint             /* Any scheduling hint set? */
    25782818        && pCfgGuest->Device.cMsSchedulingHint > msPeriod) /* This might lead to buffer underflows. */
    2579     {
    25802819        LogRel(("Audio: Warning: Scheduling hint of stream '%s' is bigger (%RU64ms) than used period size (%RU64ms)\n",
    25812820                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     */
    25832836
    25842837    /* Destroy any former mixing buffer. */
     
    25902843        AssertRCReturn(rc, rc);
    25912844    }
    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    }
    25962859
    25972860    /*
    25982861     * Init guest stream.
    25992862     */
    2600 
    26012863    if (pCfgGuest->Device.cMsSchedulingHint)
    26022864        LogRel2(("Audio: Stream '%s' got a scheduling hint of %RU32ms (%RU32 bytes)\n",
     
    26072869    AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
    26082870
    2609     /* Set the guests's default audio data layout. */
    2610     pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
    2611 
    26122871    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
    26132872    {
     
    26152874        AssertRCReturn(rc, rc);
    26162875    }
    2617 
    2618     /* Make a copy of the guest stream configuration. */
    2619     rc = PDMAudioStrmCfgCopy(&pStreamEx->Guest.Cfg, pCfgGuest);
    2620     AssertRC(rc);
    26212876
    26222877    if (RT_FAILURE(rc))
     
    27322987     * Assert sanity.
    27332988     */
    2734     AssertReturn(!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF), VERR_INVALID_FLAGS);
     2989    AssertReturn(!(fFlags & ~PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF), VERR_INVALID_FLAGS);
    27352990    AssertPtrReturn(pCfgHost,   VERR_INVALID_POINTER);
    27362991    AssertPtrReturn(pCfgGuest,  VERR_INVALID_POINTER);
     
    30993354    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, 0);
    31003355    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);
    31023357    int rc = RTCritSectEnter(&pThis->CritSect);
    31033358    AssertRCReturn(rc, 0);
     
    31083363    uint32_t cbWritable = 0;
    31093364
    3110     /* Note: We don't propage the backend stream's status to the outside -- it's the job of this
     3365    /* Note: We don't propagate the backend stream's status to the outside -- it's the job of this
    31113366     *       audio connector to make sense of it. */
    31123367    if (PDMAudioStrmStatusCanWrite(pStreamEx->Core.fStatus))
    31133368    {
    31143369        if (pStreamEx->fNoMixBufs)
     3370        {
     3371            Assert(pThis->pHostDrvAudio);
    31153372            cbWritable = pThis->pHostDrvAudio
    31163373                       ? 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        }
    31173399        else
    31183400            cbWritable = AudioMixBufFreeBytes(&pStreamEx->Host.MixBuf);
     
    31723454    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    31733455    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     3456    AssertReturn(pStreamEx->fNoMixBufs, VWRN_INVALID_STATE);
    31743457
    31753458    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);
    31773459
    31783460    AudioMixBufSetVolume(&pStreamEx->Guest.MixBuf, pVol);
     
    32483530    AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
    32493531    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    }
    32503542
    32513543    if (RT_SUCCESS(rc))
  • trunk/src/VBox/Devices/Audio/testcase/tstAudioMixBuffer.cpp

    r88356 r88433  
    548548
    549549
     550static 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
    550655/* Test 8-bit sample conversion (8-bit -> internal -> 8-bit). */
    551656static int tstConversion8(RTTEST hTest)
     
    883988    tstDownsampling(hTest, 48000, 22050);
    884989    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);
    885997
    886998    /*
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette