VirtualBox

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


Ignore:
Timestamp:
Feb 15, 2021 12:14:09 PM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
142810
Message:

Audio/HDA: Lots more code in the hope to resolve issue ticketoem2ref:36, namely:

  • Decoupled async I/O timing from guest driver-specific DMA timing to further reduce EMT workload.
  • Added data transfer heuristics (based on set-up DMA buffers) to detect Windows 10 guests (enabled by default).
  • Also expose and support 16kHz + 22,5kHz streams (16-bit signed).

Only tested on Win10 20H2 and various Ubuntu guests so far.

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

Legend:

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

    r87573 r87758  
    14421442     */
    14431443    uint64_t cTicksToNext = pStreamShared->State.cTransferTicks;
    1444     Assert(cTicksToNext == cTicksToNext);
    14451444    if (cTicksToNext) /* Only do any calculations if the stream currently is set up for transfers. */
    14461445    {
     
    18601859static VBOXSTRICTRC hdaRegWriteSDFMT(PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
    18611860{
     1861#ifdef IN_RING3
     1862    PDMAUDIOPCMPROPS Props;
     1863    int rc2 = hdaR3SDFMTToPCMProps(RT_LO_U16(u32Value), &Props);
     1864    AssertRC(rc2);
     1865    LogFunc(("[SD%RU8] Set to %#x (%RU32Hz, %RU8bit, %RU8 channel(s))\n",
     1866             HDA_SD_NUM_FROM_REG(pThis, FMT, iReg), u32Value, Props.uHz, Props.cbSample * 8 /* Bit */, Props.cChannels));
     1867
    18621868    /*
    18631869     * Write the wanted stream format into the register in any case.
     
    18701876     */
    18711877    return hdaRegWriteU16(pDevIns, pThis, iReg, u32Value);
     1878#else
     1879    RT_NOREF(pDevIns, pThis, iReg, u32Value);
     1880    return VINF_IOM_R3_MMIO_WRITE;
     1881#endif
    18721882}
    18731883
     
    26542664        fSinkActive = AudioMixerSinkIsActive(pStreamR3->pMixSink->pMixSink);
    26552665
     2666#ifdef LOG_ENABLED
     2667    const uint8_t uSD = pStreamShared->u8SD;
     2668#endif
     2669
    26562670    if (fSinkActive)
    26572671    {
    2658         uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, hTimer); /* (For virtual sync this remains the same for the whole callout IIRC) */
    2659         const bool fTimerScheduled = hdaR3StreamTransferIsScheduled(pStreamShared, tsNow);
    2660         Log3Func(("fSinksActive=%RTbool, fTimerScheduled=%RTbool\n", fSinkActive, fTimerScheduled));
     2672        const uint64_t tsNow           = PDMDevHlpTimerGet(pDevIns, hTimer); /* (For virtual sync this remains the same for the whole callout IIRC) */
     2673        const bool     fTimerScheduled = hdaR3StreamTransferIsScheduled(pStreamShared, tsNow);
     2674
     2675        Log3Func(("[SD%RU8] fSinksActive=%RTbool, fTimerScheduled=%RTbool\n", uSD, fSinkActive, fTimerScheduled));
     2676
    26612677        if (!fTimerScheduled)
    2662             hdaR3TimerSet(pDevIns, pStreamShared, tsNow + PDMDevHlpTimerGetFreq(pDevIns, hTimer) / pThis->uTimerHz,
    2663                           true /*fForce*/, tsNow /*fixed*/ );
     2678        {
     2679            if (!pStreamShared->State.tsTransferLast) /* Never did a transfer before? Initialize with current time. */
     2680                pStreamShared->State.tsTransferLast = tsNow;
     2681
     2682            Assert(tsNow >= pStreamShared->State.tsTransferLast);
     2683
     2684            /* How many ticks have passed since the last transfer? */
     2685            const uint64_t cTicksElapsed = tsNow - pStreamShared->State.tsTransferLast;
     2686
     2687            uint64_t cTicksToNext;
     2688            if (cTicksElapsed == 0) /* We just ran in the same timing slot? */
     2689            {
     2690                /* Schedule a transfer at the next regular timing slot. */
     2691                cTicksToNext = pStreamShared->State.cTransferTicks;
     2692            }
     2693            else
     2694            {
     2695                /* Some time since the last transfer has passed already; take this into account. */
     2696                if (pStreamShared->State.cTransferTicks >= cTicksElapsed)
     2697                {
     2698                    cTicksToNext = pStreamShared->State.cTransferTicks - cTicksElapsed;
     2699                }
     2700                else /* Catch up as soon as possible. */
     2701                    cTicksToNext = 0;
     2702            }
     2703
     2704            Log3Func(("[SD%RU8] tsNow=%RU64, tsTransferLast=%RU64, cTicksElapsed=%RU64 -> cTicksToNext=%RU64\n",
     2705                      uSD, tsNow, pStreamShared->State.tsTransferLast, cTicksElapsed, cTicksToNext));
     2706
     2707            /* Note: cTicksToNext can be 0, which means we have to run *now*. */
     2708            hdaR3TimerSet(pDevIns, pStreamShared, tsNow + cTicksToNext,
     2709                          true /*fForce*/, 0 /* tsNow */);
     2710        }
    26642711    }
    26652712    else
    2666         Log3Func(("fSinksActive=%RTbool\n", fSinkActive));
     2713        Log3Func(("[SD%RU8] fSinksActive=%RTbool\n", uSD, fSinkActive));
    26672714}
    26682715
     
    45884635     * Validate and read configuration.
    45894636     */
    4590     PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "BufSizeInMs|BufSizeOutMs|TimerHz|PosAdjustEnabled|PosAdjustFrames|DebugEnabled|DebugPathOut", "");
     4637    PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "BufSizeInMs|BufSizeOutMs|TimerHz|PosAdjustEnabled|PosAdjustFrames|TransferHeuristicsEnabled|DebugEnabled|DebugPathOut", "");
    45914638
    45924639    /* Note: Error checking of this value happens in hdaR3StreamSetUp(). */
    4593     int rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BufSizeInMs", &pThis->cbCircBufInMs, RT_MS_10SEC /* Default value, if not set. */);
     4640    int rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BufSizeInMs", &pThis->cbCircBufInMs, 0 /* Default value, if not set. */);
    45944641    if (RT_FAILURE(rc))
    45954642        return PDMDEV_SET_ERROR(pDevIns, rc,
     
    45974644
    45984645    /* Note: Error checking of this value happens in hdaR3StreamSetUp(). */
    4599     rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BufSizeOutMs", &pThis->cbCircBufOutMs, RT_MS_10SEC /* Default value, if not set. */);
     4646    rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BufSizeOutMs", &pThis->cbCircBufOutMs, 0 /* Default value, if not set. */);
    46004647    if (RT_FAILURE(rc))
    46014648        return PDMDEV_SET_ERROR(pDevIns, rc,
     
    46254672    if (pThis->cPosAdjustFrames)
    46264673        LogRel(("HDA: Using custom position adjustment (%RU16 audio frames)\n", pThis->cPosAdjustFrames));
     4674
     4675    rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "TransferHeuristicsEnabled", &pThis->fTransferHeuristicsEnabled, true);
     4676    if (RT_FAILURE(rc))
     4677        return PDMDEV_SET_ERROR(pDevIns, rc,
     4678                                N_("HDA configuration error: failed to read data transfer heuristics enabled as boolean"));
     4679
     4680    if (!pThis->fTransferHeuristicsEnabled)
     4681        LogRel(("HDA: Data transfer heuristics are disabled\n"));
    46274682
    46284683    rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DebugEnabled", &pThisCC->Dbg.fEnabled, false);
  • trunk/src/VBox/Devices/Audio/DevHDA.h

    r87567 r87758  
    112112    /** Whether the position adjustment is enabled or not. */
    113113    bool                    fPosAdjustEnabled;
     114    /** Whether data transfer heuristics are enabled or not.
     115     *  This tries to determine the approx. data rate a guest audio driver expects. */
     116    bool                    fTransferHeuristicsEnabled;
    114117    /** DMA position buffer enable bit. */
    115118    bool                    fDMAPosition;
  • trunk/src/VBox/Devices/Audio/DevHDACommon.h

    r87573 r87758  
    108108/** Default timer frequency (in Hz).
    109109 *
    110  * Lowering this value can ask for trouble, as backends then can run
    111  * into data underruns.
    112  *
    113  * Note: For handling surround setups (e.g. 5.1 speaker setups) we need
    114  *       a higher Hz rate, as the device emulation otherwise will come into
    115  *       timing trouble, making the output (DMA reads) crackling. */
     110 *  20 Hz now seems enough for most setups, even with load on the guest.
     111 *  Raising the rate will produce more I/O load on the guest and therefore
     112 *  also will affect the performance.
     113 */
    116114#define HDA_TIMER_HZ_DEFAULT        100
    117115
  • trunk/src/VBox/Devices/Audio/DrvAudio.cpp

    r87586 r87758  
    31973197    if (!pCfgReq->Backend.cFramesPeriod) /* Set default period size if nothing explicitly is set. */
    31983198    {
    3199         pCfgReq->Backend.cFramesPeriod = DrvAudioHlpMilliToFrames(50 /* ms */, &pCfgReq->Props);
     3199        pCfgReq->Backend.cFramesPeriod = DrvAudioHlpMilliToFrames(150 /* ms */, &pCfgReq->Props);
    32003200        RTStrPrintf(szWhat, sizeof(szWhat), "default");
    32013201    }
     
    32163216    if (!pCfgReq->Backend.cFramesBufferSize) /* Set default buffer size if nothing explicitly is set. */
    32173217    {
    3218         pCfgReq->Backend.cFramesBufferSize = DrvAudioHlpMilliToFrames(250 /* ms */, &pCfgReq->Props);
     3218        pCfgReq->Backend.cFramesBufferSize = DrvAudioHlpMilliToFrames(300 /* ms */, &pCfgReq->Props);
    32193219        RTStrPrintf(szWhat, sizeof(szWhat), "default");
    32203220    }
  • trunk/src/VBox/Devices/Audio/HDACodec.cpp

    r82968 r87758  
    947947        {
    948948            pNode->afg.node.au32F00_param[0x08] = CODEC_MAKE_F00_08(1, 0xd, 0xd);
    949             /* We set the AFG's PCM capabitilies fixed to 44.1kHz, 16-bit signed. */
    950             pNode->afg.node.au32F00_param[0x0A] = CODEC_F00_0A_44_1KHZ | CODEC_F00_0A_16_BIT;
     949            /* We set the AFG's PCM capabitilies fixed to 16kHz, 22.5kHz + 44.1kHz, 16-bit signed. */
     950            pNode->afg.node.au32F00_param[0x0A] = CODEC_F00_0A_44_1KHZ | CODEC_F00_0A_44_1KHZ_1_2X | CODEC_F00_0A_48KHZ_1_3X | CODEC_F00_0A_16_BIT;
    951951            pNode->afg.node.au32F00_param[0x0B] = CODEC_F00_0B_PCM;
    952952            pNode->afg.node.au32F00_param[0x0C] = CODEC_MAKE_F00_0C(0x17)
     
    990990        {
    991991            pNode->dac.u32A_param = CODEC_MAKE_A(HDA_SDFMT_TYPE_PCM, HDA_SDFMT_BASE_44KHZ,
    992                                                  HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_1X, HDA_SDFMT_16_BIT,
     992                                                 HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_2X, HDA_SDFMT_16_BIT,
    993993                                                 HDA_SDFMT_CHAN_STEREO);
    994994
     
    10291029
    10301030            pNode->adc.u32A_param   = CODEC_MAKE_A(HDA_SDFMT_TYPE_PCM, HDA_SDFMT_BASE_44KHZ,
    1031                                                    HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_1X, HDA_SDFMT_16_BIT,
     1031                                                   HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_2X, HDA_SDFMT_16_BIT,
    10321032                                                   HDA_SDFMT_CHAN_STEREO);
    10331033
     
    10511051        {
    10521052            pNode->spdifout.u32A_param   = CODEC_MAKE_A(HDA_SDFMT_TYPE_PCM, HDA_SDFMT_BASE_44KHZ,
    1053                                                         HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_1X, HDA_SDFMT_16_BIT,
     1053                                                        HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_2X, HDA_SDFMT_16_BIT,
    10541054                                                        HDA_SDFMT_CHAN_STEREO);
    10551055            pNode->spdifout.u32F06_param = 0;
     
    10701070        {
    10711071            pNode->spdifin.u32A_param = CODEC_MAKE_A(HDA_SDFMT_TYPE_PCM, HDA_SDFMT_BASE_44KHZ,
    1072                                                      HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_1X, HDA_SDFMT_16_BIT,
     1072                                                     HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_2X, HDA_SDFMT_16_BIT,
    10731073                                                     HDA_SDFMT_CHAN_STEREO);
    10741074
  • trunk/src/VBox/Devices/Audio/HDAStream.cpp

    r87638 r87758  
    301301#endif
    302302
    303     /*
    304      * Set the stream's timer Hz rate, based on the stream channel count.
    305      * Currently this is just a rough guess and we might want to optimize this further.
    306      *
    307      * In any case, more channels per SDI/SDO means that we have to drive data more frequently.
    308      */
    309     if (pThis->uTimerHz == HDA_TIMER_HZ_DEFAULT) /* Make sure that we don't have any custom Hz rate set we want to enforce */
    310     {
    311         if (Props.cChannels >= 5)
    312             pStreamShared->State.uTimerHz = 300;
    313         else if (Props.cChannels == 4)
    314             pStreamShared->State.uTimerHz = 150;
    315         else
    316             pStreamShared->State.uTimerHz = 100;
    317     }
    318     else
    319         pStreamShared->State.uTimerHz = pThis->uTimerHz;
    320 
    321     /* Did some of the vital / critical parameters change?
    322      * If not, we can skip a lot of the (re-)initialization and just (re-)use the existing stuff.
    323      * Also, tell the caller so that further actions can be taken. */
    324     if (   uSD        == pStreamShared->u8SD   /* paranoia OFC */
    325         && u64BDLBase == pStreamShared->u64BDLBase
    326         && u16LVI     == pStreamShared->u16LVI
    327         && u32CBL     == pStreamShared->u32CBL
    328         && u8FIFOS    == pStreamShared->u8FIFOS
    329         && u8FIFOW    == pStreamShared->u8FIFOW
    330         && u16FMT     == pStreamShared->u16FMT)
    331     {
    332         LogFunc(("[SD%RU8] No format change, skipping (re-)initialization\n", uSD));
    333         return VINF_NO_CHANGE;
    334     }
    335 
    336303    /* Make sure the guest behaves regarding the stream's FIFO. */
    337304    ASSERT_GUEST_LOGREL_MSG_STMT(u8FIFOW <= u8FIFOS,
     
    351318    PPDMAUDIOSTREAMCFG pCfg = &pStreamShared->State.Cfg;
    352319    pCfg->Props = Props;
    353 
    354     /* (Re-)Allocate the stream's internal DMA buffer, based on the PCM  properties we just got above. */
    355     if (pStreamR3->State.pCircBuf)
    356     {
    357         RTCircBufDestroy(pStreamR3->State.pCircBuf);
    358         pStreamR3->State.pCircBuf = NULL;
    359     }
    360 
    361     const uint32_t cbCircBufDefault = DrvAudioHlpMilliToBytes(RT_MS_10SEC, &pCfg->Props);
    362 
    363     uint32_t cbCircBuf = DrvAudioHlpMilliToBytes(  hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN
    364                                                  ? pThis->cbCircBufInMs : pThis->cbCircBufOutMs, &pCfg->Props);
    365 
    366     ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf,
    367                                  ("Ring buffer size for stream #%RU8 is invalid (%zu), setting to default\n", uSD, cbCircBuf),
    368                                  cbCircBuf = cbCircBufDefault);
    369     ASSERT_GUEST_LOGREL_MSG_STMT(DrvAudioHlpBytesIsAligned(cbCircBuf, &pCfg->Props),
    370                                  ("Ring buffer size for stream #%RU8 is misaligned (%zu), setting to default\n", uSD, cbCircBuf),
    371                                  cbCircBuf = cbCircBufDefault);
    372 
    373     if (cbCircBuf != cbCircBufDefault)
    374         LogRel2(("HDA: Stream #%RU8 is using a custom ring buffer size of %RU64ms (%RU32 bytes)\n",
    375                  uSD, DrvAudioHlpBytesToMilli(cbCircBuf, &pCfg->Props), cbCircBuf));
    376 
    377     rc = RTCircBufCreate(&pStreamR3->State.pCircBuf, cbCircBuf);
    378     AssertRCReturn(rc, rc);
    379320
    380321    /* Set the stream's direction. */
     
    400341
    401342        default:
    402             rc = VERR_NOT_SUPPORTED;
     343            AssertFailedReturn(VERR_NOT_SUPPORTED);
    403344            break;
    404345    }
    405346
     347    /* Assign the global device rate to the stream. */
     348    pStreamShared->State.uTimerIoHz = pThis->uTimerHz;
     349
    406350    /* Set scheduling hint (if available). */
    407     if (pStreamShared->State.uTimerHz)
    408         pCfg->Device.cMsSchedulingHint = 1000 /* ms */ / pStreamShared->State.uTimerHz;
    409 
    410     LogFunc(("[SD%RU8] DMA @ 0x%x (%RU32 bytes), LVI=%RU16, FIFOS=%RU8\n",
    411              uSD, pStreamShared->u64BDLBase, pStreamShared->u32CBL, pStreamShared->u16LVI, pStreamShared->u8FIFOS));
    412 
     351    if (pStreamShared->State.uTimerIoHz)
     352        pCfg->Device.cMsSchedulingHint = RT_MS_1SEC / pStreamShared->State.uTimerIoHz;
     353
     354    LogRel2(("HDA: Stream #%RU8 DMA @ 0x%x (%RU32 bytes = %RU64ms total)\n",
     355             uSD, pStreamShared->u64BDLBase, pStreamShared->u32CBL,
     356             DrvAudioHlpBytesToMilli(pStreamShared->u32CBL, &pStreamShared->State.Cfg.Props)));
     357
     358    /* Make sure that the chosen Hz rate dividable by the stream's rate. */
     359    if (pStreamShared->State.Cfg.Props.uHz % pStreamShared->State.uTimerIoHz != 0)
     360        LogRel(("HDA: Stream #%RU8 timer Hz rate (%RU32) does not fit to stream #%RU8 timing (%RU32)\n",
     361                uSD, pStreamShared->State.uTimerIoHz, uSD, pStreamShared->State.Cfg.Props.uHz));
     362
     363    /* Figure out how many transfer fragments we're going to use for this stream. */
     364    uint8_t cTransferFragments = pStreamShared->u16LVI + 1;
     365    if (cTransferFragments <= 1)
     366        LogRel(("HDA: Warning: Stream #%RU8 transfer fragments (%RU8) invalid -- buggy guest audio driver!\n",
     367                uSD, pStreamShared->u16LVI));
     368
     369    /*
     370     * Handle the stream's position adjustment.
     371     */
     372    uint32_t cfPosAdjust = 0;
     373
     374    LogFunc(("[SD%RU8] fPosAdjustEnabled=%RTbool, cPosAdjustFrames=%RU16\n",
     375             uSD, pThis->fPosAdjustEnabled, pThis->cPosAdjustFrames));
     376
     377    if (pThis->fPosAdjustEnabled) /* Is the position adjustment enabled at all? */
     378    {
     379        HDABDLE BDLE;
     380        RT_ZERO(BDLE);
     381
     382        int rc2 = hdaR3BDLEFetch(pDevIns, &BDLE, pStreamShared->u64BDLBase, 0 /* Entry */);
     383        AssertRC(rc2);
     384
     385        /* Note: Do *not* check if this BDLE aligns to the stream's frame size.
     386         *       It can happen that this isn't the case on some guests, e.g.
     387         *       on Windows with a 5.1 speaker setup.
     388         *
     389         *       The only thing which counts is that the stream's CBL value
     390         *       properly aligns to the stream's frame size.
     391         */
     392
     393        /* If no custom set position adjustment is set, apply some
     394         * simple heuristics to detect the appropriate position adjustment. */
     395        if (   !pThis->cPosAdjustFrames
     396        /* Position adjustmenet buffer *must* have the IOC bit set! */
     397            && hdaR3BDLENeedsInterrupt(&BDLE))
     398        {
     399            /** @todo Implement / use a (dynamic) table once this gets more complicated. */
     400#ifdef VBOX_WITH_INTEL_HDA
     401            /* Intel ICH / PCH: 1 frame. */
     402            if (BDLE.Desc.u32BufSize == (uint32_t)(1 * pStreamR3->State.Mapping.cbFrameSize))
     403            {
     404                cfPosAdjust = 1;
     405            }
     406            /* Intel Baytrail / Braswell: 32 frames. */
     407            else if (BDLE.Desc.u32BufSize == (uint32_t)(32 * pStreamR3->State.Mapping.cbFrameSize))
     408            {
     409                cfPosAdjust = 32;
     410            }
     411#endif
     412        }
     413        else /* Go with the set default. */
     414            cfPosAdjust = pThis->cPosAdjustFrames;
     415
     416        if (cfPosAdjust)
     417        {
     418            /* Also adjust the number of fragments, as the position adjustment buffer
     419             * does not count as an own fragment as such.
     420             *
     421             * This e.g. can happen on (newer) Ubuntu guests which use
     422             * 4 (IOC) + 4408 (IOC) + 4408 (IOC) + 4408 (IOC) + 4404 (= 17632) bytes,
     423             * where the first buffer (4) is used as position adjustment.
     424             *
     425             * Only skip a fragment if the whole buffer fragment is used for
     426             * position adjustment.
     427             */
     428            if ((cfPosAdjust * pStreamR3->State.Mapping.cbFrameSize) == BDLE.Desc.u32BufSize)
     429                cTransferFragments--;
     430
     431            /* Initialize position adjustment counter. */
     432            pStreamShared->State.cfPosAdjustDefault = cfPosAdjust;
     433            pStreamShared->State.cfPosAdjustLeft    = pStreamShared->State.cfPosAdjustDefault;
     434
     435            LogRel2(("HDA: Position adjustment for stream #%RU8 active (%RU32 frames)\n",
     436                     uSD, pStreamShared->State.cfPosAdjustDefault));
     437        }
     438    }
     439
     440    Log3Func(("[SD%RU8] cfPosAdjust=%RU32, cFragments=%RU8\n", uSD, cfPosAdjust, cTransferFragments));
     441
     442    /*
     443     * Set up data transfer stuff.
     444     */
     445
     446    /* Prevent division by zero. */
     447    ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.uTimerIoHz,
     448                                 ("Timer Hz rate for stream #%RU8 is invalid\n", uSD),
     449                                 pStreamShared->State.uTimerIoHz = HDA_TIMER_HZ_DEFAULT);
     450    /*
     451     * Determine the transfer Hz the guest OS expects data transfer at.
     452     *
     453     * Guests also expect a very extact DMA timing for reading / writing audio data, so we run on a constant
     454     * (virtual) rate which we expose to the guest.
     455     *
     456     * Data rate examples:
     457     *   * Windows 10 @ 44,1kHz / 16-bit stereo
     458     *      * Default mode: 448 audio frames -> ~10.15ms) = 1792 byte every ~10ms.
     459     *      * Fast mode:    128 audio frames -> ~ 2.90ms) =  512 byte every ~3ms.
     460     */
     461
     462    /* The transfer Hz depend on the heuristics above, that is,
     463       how often the guest expects to see a new data transfer. */
     464    unsigned uTransferHz;
     465
     466    if (pThis->fTransferHeuristicsEnabled) /* Are data transfer heuristics enabled? */
     467    {
     468
     469        /* Use the whole CBL as a starting point.
     470         * This basically ASSUMES that we have one consequtive buffer with only one interrupt at the end. */
     471        uint32_t cbTransferHeuristicsMin       = pStreamShared->u32CBL;
     472
     473        /* Don't take frames (as bytes) into account which are part of the position adjustment. */
     474        uint32_t cbTransferHeuristicsPosAdjust = pStreamShared->State.cfPosAdjustDefault * pStreamR3->State.Mapping.cbFrameSize;
     475
     476        HDABDLEDESC bd;
     477        uint32_t    cbTransferHeuristicsCur = 0;
     478        for (uint8_t i = 0; i < cTransferFragments; i++)
     479        {
     480            PDMDevHlpPhysRead(pDevIns, u64BDLBase + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));
     481
     482            /* Position adjustment (still) needed / active? */
     483            if (cbTransferHeuristicsPosAdjust)
     484            {
     485                const uint32_t cbTransferHeuristicsPosAdjustMin = RT_MIN(cbTransferHeuristicsPosAdjust, bd.u32BufSize);
     486
     487                bd.u32BufSize                 -= cbTransferHeuristicsPosAdjustMin;
     488                cbTransferHeuristicsPosAdjust -= cbTransferHeuristicsPosAdjustMin;
     489            }
     490
     491            /* Anything left to process for the current BDLE after doing the position adjustment? */
     492            if (bd.u32BufSize == 0)
     493                continue;
     494
     495            /* Is an interrupt expected for the current BDLE? */
     496            if (bd.fFlags & HDA_BDLE_F_IOC)
     497            {
     498                cbTransferHeuristicsCur += bd.u32BufSize;
     499                cbTransferHeuristicsMin  = RT_MIN(cbTransferHeuristicsCur, cbTransferHeuristicsMin);
     500                cbTransferHeuristicsCur  = 0;
     501            }
     502            else /* No interrupt expected -> add it to the former BDLE size. */
     503                cbTransferHeuristicsCur += bd.u32BufSize;
     504        }
     505
     506        /* !!! HACK ALERT BEGIN !!! */
     507
     508        /* Windows 10's audio driver expects a transfer all ~10.1ms (~1764 bytes), although
     509         * it sets up 1792 bytes per BDLE.
     510         *
     511         * I currently don't have any clue why it does this that way, so try to simply detect this
     512         * and alter the value so that we get a somewhat proper audio output. */
     513        if (cbTransferHeuristicsMin == 1792)
     514        {
     515            LogRel2(("HDA: Guest seems to be Windows 10 -- setting a fixed transfer minimum size\n"));
     516            cbTransferHeuristicsMin = 1764;
     517        }
     518
     519        /* !!! HACK ALERT END !!! */
     520
     521        uint32_t msTransferHeuristicsMin = DrvAudioHlpBytesToMilli(cbTransferHeuristicsMin, &pCfg->Props);
     522
     523        /* Prevent division by zero. */
     524        ASSERT_GUEST_LOGREL_MSG_STMT(msTransferHeuristicsMin,
     525                                     ("Transfer heuristics for stream #%RU8 buggy\n", uSD),
     526                                     msTransferHeuristicsMin = 10 /* ms, equals 100 Hz */);
     527
     528        uTransferHz = RT_MS_1SEC / msTransferHeuristicsMin;
     529
     530        LogRel2(("HDA: Stream #%RU8 needs a data transfer at least every %RU64ms (%RU32 bytes) -- transfers run at %u Hz\n",
     531                 uSD, msTransferHeuristicsMin, cbTransferHeuristicsMin, uTransferHz));
     532    }
     533    else
     534    {
     535        /* Use I/O timing rate instead. */
     536        uTransferHz = pStreamShared->State.uTimerIoHz;
     537    }
     538
     539    if (uTransferHz > 400) /* Anything above 400 Hz looks fishy -- tell the user. */
     540        LogRel(("HDA: Calculated transfer Hz rate for stream #%RU8 looks incorrect (%u), please re-run with audio debug mode and report a bug\n",
     541                uSD, uTransferHz));
     542
     543    pStreamShared->State.cbTransferSize =
     544        (pStreamShared->State.Cfg.Props.uHz * pStreamR3->State.Mapping.cbFrameSize) / uTransferHz;
     545    ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferSize,
     546                                 ("Transfer size for stream #%RU8 is invalid\n", uSD), rc = VERR_INVALID_PARAMETER);
    413547    if (RT_SUCCESS(rc))
    414548    {
    415         /* Make sure that the chosen Hz rate dividable by the stream's rate. */
    416         if (pStreamShared->State.Cfg.Props.uHz % pStreamShared->State.uTimerHz != 0)
    417             LogRel(("HDA: Stream timer Hz rate (%RU32) does not fit to stream #%RU8 timing (%RU32)\n",
    418                     pStreamShared->State.uTimerHz, uSD, pStreamShared->State.Cfg.Props.uHz));
    419 
    420         /* Figure out how many transfer fragments we're going to use for this stream. */
    421         /** @todo Use a more dynamic fragment size? */
    422         uint8_t cFragments = pStreamShared->u16LVI + 1;
    423         if (cFragments <= 1)
    424             cFragments = 2; /* At least two fragments (BDLEs) must be present. */
    425 
    426549        /*
    427          * Handle the stream's position adjustment.
     550         * Calculate the bytes we need to transfer to / from the stream's DMA per iteration.
     551         * This is bound to the device's Hz rate and thus to the (virtual) timing the device expects.
     552         *
     553         * As we don't do chunked transfers the moment, the chunk size equals the overall transfer size.
    428554         */
    429         uint32_t cfPosAdjust = 0;
    430 
    431         LogFunc(("[SD%RU8] fPosAdjustEnabled=%RTbool, cPosAdjustFrames=%RU16\n",
    432                  uSD, pThis->fPosAdjustEnabled, pThis->cPosAdjustFrames));
    433 
    434         if (pThis->fPosAdjustEnabled) /* Is the position adjustment enabled at all? */
    435         {
    436             HDABDLE BDLE;
    437             RT_ZERO(BDLE);
    438 
    439             int rc2 = hdaR3BDLEFetch(pDevIns, &BDLE, pStreamShared->u64BDLBase, 0 /* Entry */);
    440             AssertRC(rc2);
    441 
    442             /* Note: Do *not* check if this BDLE aligns to the stream's frame size.
    443              *       It can happen that this isn't the case on some guests, e.g.
    444              *       on Windows with a 5.1 speaker setup.
    445              *
    446              *       The only thing which counts is that the stream's CBL value
    447              *       properly aligns to the stream's frame size.
    448              */
    449 
    450             /* If no custom set position adjustment is set, apply some
    451              * simple heuristics to detect the appropriate position adjustment. */
    452             if (   !pThis->cPosAdjustFrames
    453             /* Position adjustmenet buffer *must* have the IOC bit set! */
    454                 && hdaR3BDLENeedsInterrupt(&BDLE))
    455             {
    456                 /** @todo Implement / use a (dynamic) table once this gets more complicated. */
    457 #ifdef VBOX_WITH_INTEL_HDA
    458                 /* Intel ICH / PCH: 1 frame. */
    459                 if (BDLE.Desc.u32BufSize == (uint32_t)(1 * pStreamR3->State.Mapping.cbFrameSize))
    460                 {
    461                     cfPosAdjust = 1;
    462                 }
    463                 /* Intel Baytrail / Braswell: 32 frames. */
    464                 else if (BDLE.Desc.u32BufSize == (uint32_t)(32 * pStreamR3->State.Mapping.cbFrameSize))
    465                 {
    466                     cfPosAdjust = 32;
    467                 }
     555        pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize;
     556        ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferChunk,
     557                                     ("Transfer chunk for stream #%RU8 is invalid\n", uSD),
     558                                     rc = VERR_INVALID_PARAMETER);
     559        if (RT_SUCCESS(rc))
     560        {
     561            /* Make sure that the transfer chunk does not exceed the overall transfer size. */
     562            AssertStmt(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize,
     563                       pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize);
     564
     565            const uint64_t uTimerFreq  = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer);
     566
     567            const double cTicksPerHz   = uTimerFreq  / pStreamShared->State.uTimerIoHz;
     568                  double cTicksPerByte = cTicksPerHz / (double)pStreamShared->State.cbTransferChunk;
     569
     570            if (pStreamShared->State.uTimerIoHz < uTransferHz)
     571                cTicksPerByte /= uTransferHz / pStreamShared->State.uTimerIoHz;
     572            else
     573                cTicksPerByte *= pStreamShared->State.uTimerIoHz / uTransferHz;
     574
     575            Assert(cTicksPerByte);
     576
     577#define HDA_ROUND_NEAREST(a_X) ((a_X) >= 0 ? (uint32_t)((a_X) + 0.5) : (uint32_t)((a_X) - 0.5)) /** @todo r=andy Do we have rounding in IPRT? */
     578
     579            /* Calculate the timer ticks per byte for this stream. */
     580            pStreamShared->State.cTicksPerByte = HDA_ROUND_NEAREST(cTicksPerByte);
     581            Assert(pStreamShared->State.cTicksPerByte);
     582
     583            const double cTransferTicks = pStreamShared->State.cbTransferChunk * cTicksPerByte;
     584
     585            /* Calculate timer ticks per transfer. */
     586            pStreamShared->State.cTransferTicks = HDA_ROUND_NEAREST(cTransferTicks);
     587            Assert(pStreamShared->State.cTransferTicks);
     588
     589#undef HDA_ROUND_NEAREST
     590
     591            LogRel2(("HDA: Stream #%RU8 is using %uHz I/O timer (%RU64 virtual ticks / Hz), stream Hz=%RU32, "
     592                     "cTicksPerByte=%RU64, cTransferTicks=%RU64 -> cbTransferChunk=%RU32 (%RU64ms), cbTransferSize=%RU32 (%RU64ms)\n",
     593                     uSD, pStreamShared->State.uTimerIoHz, (uint64_t)cTicksPerHz, pStreamShared->State.Cfg.Props.uHz,
     594                     pStreamShared->State.cTicksPerByte, pStreamShared->State.cTransferTicks,
     595                     pStreamShared->State.cbTransferChunk, DrvAudioHlpBytesToMilli(pStreamShared->State.cbTransferChunk, &pCfg->Props),
     596                     pStreamShared->State.cbTransferSize,  DrvAudioHlpBytesToMilli(pStreamShared->State.cbTransferSize,  &pCfg->Props)));
     597
     598            /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */
     599            hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD));
     600
     601#ifdef LOG_ENABLED
     602            hdaR3BDLEDumpAll(pDevIns, pThis, pStreamShared->u64BDLBase, pStreamShared->u16LVI + 1);
    468603#endif
    469             }
    470             else /* Go with the set default. */
    471                 cfPosAdjust = pThis->cPosAdjustFrames;
    472 
    473             if (cfPosAdjust)
    474             {
    475                 /* Also adjust the number of fragments, as the position adjustment buffer
    476                  * does not count as an own fragment as such.
    477                  *
    478                  * This e.g. can happen on (newer) Ubuntu guests which use
    479                  * 4 (IOC) + 4408 (IOC) + 4408 (IOC) + 4408 (IOC) + 4404 (= 17632) bytes,
    480                  * where the first buffer (4) is used as position adjustment.
    481                  *
    482                  * Only skip a fragment if the whole buffer fragment is used for
    483                  * position adjustment.
    484                  */
    485                 if (   (cfPosAdjust * pStreamR3->State.Mapping.cbFrameSize) == BDLE.Desc.u32BufSize
    486                     && cFragments)
    487                 {
    488                     cFragments--;
    489                 }
    490 
    491                 /* Initialize position adjustment counter. */
    492                 pStreamShared->State.cfPosAdjustDefault = cfPosAdjust;
    493                 pStreamShared->State.cfPosAdjustLeft    = pStreamShared->State.cfPosAdjustDefault;
    494 
    495                 LogRel2(("HDA: Position adjustment for stream #%RU8 active (%RU32 frames)\n",
    496                          uSD, pStreamShared->State.cfPosAdjustDefault));
    497             }
    498         }
    499 
    500         LogFunc(("[SD%RU8] cfPosAdjust=%RU32, cFragments=%RU8\n", uSD, cfPosAdjust, cFragments));
     604        }
     605    }
     606
     607    /*
     608     * Set up internal ring buffer.
     609     */
     610    if (RT_SUCCESS(rc))
     611    {
     612        /* (Re-)Allocate the stream's internal DMA buffer,
     613         * based on the timing *and* PCM properties we just got above. */
     614        if (pStreamR3->State.pCircBuf)
     615        {
     616            RTCircBufDestroy(pStreamR3->State.pCircBuf);
     617            pStreamR3->State.pCircBuf = NULL;
     618        }
    501619
    502620        /*
    503          * Set up data transfer stuff.
     621         * The default size of our internal ring buffer depends on the transfer timing
     622         * we have to reach in order to make the guest driver happy *and* on the I/O timing.
     623         *
     624         * We always use triple the minimum timing of both timings for safety (triple buffering),
     625         * otherwise we risk running into buffer overflows.
    504626         */
    505 
    506         /* Prevent division by zero. */
    507         ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.uTimerHz,
    508                                      ("Timer Hz rate for stream #%RU8 is invalid\n", uSD),
    509                                      pStreamShared->State.uTimerHz = HDA_TIMER_HZ_DEFAULT);
    510         /*
    511          * Calculate the fragment size the guest OS expects interrupt delivery at.
    512          *
    513          * Guests also expect a very extact DMA timing for reading / writing audio data, so we run on a constant
    514          * (virtual) rate which we expose to the guest.
    515          *
    516          * Data rate examples:
    517          *   * Windows 10 @ 44,1kHz / 16-bit stereo
    518          *      * Default mode: 448 audio frames (~10.15ms) = 1792 byte / ~10ms.
    519          *      * Fast mode:    128 audio frames (~ 2.90ms) =  512 byte / ~3ms.
    520          */
    521         pStreamShared->State.cbTransferSize
    522             = (pStreamShared->State.Cfg.Props.uHz * pStreamR3->State.Mapping.cbFrameSize) / pStreamShared->State.uTimerHz;
    523         ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferSize,
    524                                      ("Transfer size for stream #%RU8 is invalid\n", uSD), rc = VERR_INVALID_PARAMETER);
     627        const unsigned uTransferHzMin = RT_MIN(uTransferHz, pStreamShared->State.uTimerIoHz);
     628
     629              uint32_t cbCircBuf;
     630        const uint32_t cbCircBufDefault = DrvAudioHlpMilliToBytes((RT_MS_1SEC / uTransferHzMin) * 3, &pCfg->Props);
     631
     632        LogRel2(("HDA: Stream #%RU8 default ring buffer size is %RU64ms (%RU32 bytes)\n",
     633                 uSD, DrvAudioHlpBytesToMilli(cbCircBufDefault, &pCfg->Props), cbCircBufDefault));
     634
     635        uint32_t cbCircBufGlobal = DrvAudioHlpMilliToBytes(  hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN
     636                                                           ? pThis->cbCircBufInMs : pThis->cbCircBufOutMs, &pCfg->Props);
     637        if (cbCircBufGlobal) /* Anything set via CFGM? */
     638        {
     639
     640            ASSERT_GUEST_LOGREL_MSG_STMT(DrvAudioHlpBytesIsAligned(cbCircBufGlobal, &pCfg->Props),
     641                                         ("Ring buffer size for stream #%RU8 is misaligned (%zu), setting to default\n", uSD, cbCircBufGlobal),
     642                                         cbCircBufGlobal = cbCircBufDefault);
     643
     644            LogRel2(("HDA: Stream #%RU8 is using a custom ring buffer size of %RU64ms (%RU32 bytes)\n",
     645                     uSD, DrvAudioHlpBytesToMilli(cbCircBufGlobal, &pCfg->Props), cbCircBufGlobal));
     646
     647            cbCircBuf = cbCircBufGlobal;
     648        }
     649        else
     650            cbCircBuf = cbCircBufDefault;
     651
     652        ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf,
     653                                     ("Ring buffer size for stream #%RU8 is invalid\n", uSD),
     654                                     rc = VERR_INVALID_PARAMETER);
    525655        if (RT_SUCCESS(rc))
    526         {
    527             /*
    528              * Calculate the bytes we need to transfer to / from the stream's DMA per iteration.
    529              * This is bound to the device's Hz rate and thus to the (virtual) timing the device expects.
    530              *
    531              * As we don't do chunked transfers the moment, the chunk size equals the overall transfer size.
    532              */
    533             pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize;
    534             ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferChunk,
    535                                          ("Transfer chunk for stream #%RU8 is invalid\n", uSD),
    536                                          rc = VERR_INVALID_PARAMETER);
    537             if (RT_SUCCESS(rc))
    538             {
    539                 /* Make sure that the transfer chunk does not exceed the overall transfer size. */
    540                 AssertStmt(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize,
    541                            pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize);
    542 
    543                 const uint64_t uTimerFreq = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer);
    544 
    545                 const double cTicksPerHz   = uTimerFreq  / pStreamShared->State.uTimerHz;
    546                 const double cTicksPerByte = cTicksPerHz / (double)pStreamShared->State.cbTransferChunk;
    547 
    548 #define HDA_ROUND_NEAREST(a_X) ((a_X) >= 0 ? (uint32_t)((a_X) + 0.5) : (uint32_t)((a_X) - 0.5)) /** @todo r=andy Do we have rounding in IPRT? */
    549 
    550                 /* Calculate the timer ticks per byte for this stream. */
    551                 pStreamShared->State.cTicksPerByte = HDA_ROUND_NEAREST(cTicksPerByte);
    552                 Assert(pStreamShared->State.cTicksPerByte);
    553 
    554                 const double cTransferTicks = pStreamShared->State.cbTransferChunk * cTicksPerByte;
    555 
    556                 /* Calculate timer ticks per transfer. */
    557                 pStreamShared->State.cTransferTicks = HDA_ROUND_NEAREST(cTransferTicks);
    558                 Assert(pStreamShared->State.cTransferTicks);
    559 
    560 #undef HDA_ROUND_NEAREST
    561 
    562                 LogRel2(("HDA: Stream #%RU8 is using %uHz device timer (%RU64 virtual ticks / Hz), stream Hz=%RU32, "
    563                          "cTicksPerByte=%RU64, cTransferTicks=%RU64 -> cbTransferChunk=%RU32 (%RU64ms), cbTransferSize=%RU32 (%RU64ms)\n",
    564                          uSD, pStreamShared->State.uTimerHz, (uint64_t)cTicksPerHz, pStreamShared->State.Cfg.Props.uHz,
    565                          pStreamShared->State.cTicksPerByte, pStreamShared->State.cTransferTicks,
    566                          pStreamShared->State.cbTransferChunk, DrvAudioHlpBytesToMilli(pStreamShared->State.cbTransferChunk, &pCfg->Props),
    567                          pStreamShared->State.cbTransferSize,  DrvAudioHlpBytesToMilli(pStreamShared->State.cbTransferSize,  &pCfg->Props)));
    568 
    569                 /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */
    570                 hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD));
    571 
    572 #ifdef LOG_ENABLED
    573                 hdaR3BDLEDumpAll(pDevIns, pThis, pStreamShared->u64BDLBase, pStreamShared->u16LVI + 1);
    574 #endif
    575             }
    576         }
     656            rc = RTCircBufCreate(&pStreamR3->State.pCircBuf, cbCircBuf);
    577657    }
    578658
     
    641721    pStreamShared->State.tsTransferNext = 0;
    642722
    643     /* Initialize other timestamps. */
    644     pStreamShared->State.tsLastUpdateNs = 0;
     723    /* Initialize timestamps. */
     724    pStreamShared->State.tsLastTransferNs = 0;
     725    pStreamShared->State.tsLastReadNs     = 0;
    645726
    646727    RT_ZERO(pStreamShared->State.BDLE);
     
    10461127    }
    10471128
     1129    /* Update real-time timestamp. */
     1130    const uint64_t tsNowNs = RTTimeNanoTS();
     1131#ifdef LOG_ENABLED
     1132    const uint64_t tsDeltaMs = (tsNowNs - pStreamShared->State.tsLastTransferNs) / RT_NS_1MS;
     1133    Log3Func(("[SD%RU8] tsDeltaNs=%RU64ms\n", uSD, tsDeltaMs));
     1134#endif
     1135    pStreamShared->State.tsLastTransferNs = tsNowNs;
     1136
    10481137    const uint64_t tsNow = PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer);
    10491138
    10501139    if (!pStreamShared->State.tsTransferLast)
    10511140        pStreamShared->State.tsTransferLast = tsNow;
    1052 
    1053 #ifdef DEBUG
    1054     const int64_t iTimerDelta = tsNow - pStreamShared->State.tsTransferLast;
    1055     Log3Func(("[SD%RU8] Time now=%RU64, last=%RU64 -> %RI64 ticks delta\n",
    1056               uSD, tsNow, pStreamShared->State.tsTransferLast, iTimerDelta));
    1057 #endif
    10581141
    10591142    pStreamShared->State.tsTransferLast = tsNow;
     
    10971180    uint32_t cbProcessed = 0;
    10981181    uint32_t cbLeft      = cbToProcess;
     1182
     1183    /* Whether an interrupt has been sent (asserted) for this transfer period already or not.
     1184     *
     1185     * Note: Windows 10 relies on this, e.g. sending more than one interrupt per transfer period
     1186     *       confuses the Windows' audio driver and will screw up the audio data. So only send
     1187     *       one interrupt per transfer period.
     1188     */
     1189    bool fInterruptSent = false;
    10991190
    11001191    while (cbLeft)
     
    13701461                if (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_IOCE)
    13711462                {
    1372                     pStreamShared->State.cTransferPendingInterrupts++;
    1373 
    1374                     AssertMsg(pStreamShared->State.cTransferPendingInterrupts <= 32,
    1375                               ("Too many pending interrupts (%RU8) for stream #%RU8\n",
    1376                                pStreamShared->State.cTransferPendingInterrupts, uSD));
    1377 
    13781463                    /* Assert the interrupt before actually fetching the next BDLE below. */
    1379                     if (pStreamShared->State.cTransferPendingInterrupts)
     1464                    if (!fInterruptSent)
    13801465                    {
     1466                        pStreamShared->State.cTransferPendingInterrupts++;
     1467
     1468                        AssertMsg(pStreamShared->State.cTransferPendingInterrupts <= 32,
     1469                                  ("Too many pending interrupts (%RU8) for stream #%RU8\n",
     1470                                   pStreamShared->State.cTransferPendingInterrupts, uSD));
     1471
    13811472                        Log3Func(("[SD%RU8] Scheduling interrupt (now %RU8 total)\n", uSD, pStreamShared->State.cTransferPendingInterrupts));
    13821473
     
    13971488                         * ending / beginning a period. */
    13981489                        HDA_PROCESS_INTERRUPT(pDevIns, pThis);
     1490
     1491                        fInterruptSent = true;
    13991492                    }
    14001493                }
     
    14631556        /* No, set relative timer ticks for the next transfer to happen.
    14641557         * This must happen at a constant rate. */
    1465         tsTransferNext = pStreamShared->State.cTransferTicks;
     1558        tsTransferNext = tsNow + pStreamShared->State.cTransferTicks;
    14661559    }
    14671560
     
    14691562    if (tsTransferNext) /* Can be 0 if no next transfer is needed. */
    14701563    {
    1471         Log3Func(("[SD%RU8] Scheduling timer @ %RU64\n", uSD, tsNow + tsTransferNext));
     1564        Log3Func(("[SD%RU8] Scheduling timer @ %RU64\n", uSD, tsTransferNext));
     1565
     1566        /* Make sure to assign next transfer timestamp before setting the timer. */
     1567        pStreamShared->State.tsTransferNext = tsTransferNext;
    14721568
    14731569        Assert(!fInTimer || tsNow == PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer));
    14741570        hdaR3TimerSet(pDevIns, pStreamShared, tsTransferNext,
    14751571                      true /* fForce - skip tsTransferNext check */, fInTimer ? tsNow : 0);
    1476 
    1477         pStreamShared->State.tsTransferNext = tsTransferNext;
    14781572    }
    14791573
     
    15321626        return;
    15331627
     1628    const uint64_t tsNowNs = RTTimeNanoTS();
     1629
    15341630    int rc2;
    15351631
     
    15511647                /* When hitting an overflow, drop all remaining data to make space for current data.
    15521648                 * This is needed in order to keep the device emulation running at a constant rate,
    1553                  * at the cost of losing old data. */
     1649                 * at the cost of losing valid (but too much) data. */
    15541650                RTCircBufReset(pStreamR3->State.pCircBuf);
    15551651                cbStreamFree = hdaR3StreamGetFree(pStreamR3);
     
    15601656            AssertRC(rc2);
    15611657
    1562             const uint64_t tsNowNs = RTTimeNanoTS();
    1563 
    1564             /* Never updated yet? Set initial timestamp. */
    1565             if (pStreamShared->State.tsLastUpdateNs == 0)
    1566                 pStreamShared->State.tsLastUpdateNs = tsNowNs;
     1658            /* Never read yet? Set initial timestamp. */
     1659            if (pStreamShared->State.tsLastReadNs == 0)
     1660                pStreamShared->State.tsLastReadNs = tsNowNs;
    15671661
    15681662            /* Only read from the HDA stream at the given scheduling rate. */
    1569             if (tsNowNs - pStreamShared->State.tsLastUpdateNs >= pStreamShared->State.Cfg.Device.cMsSchedulingHint * RT_NS_1MS)
    1570             {
     1663            Assert(tsNowNs >= pStreamShared->State.tsLastReadNs);
     1664            const uint64_t tsDeltaMs = (tsNowNs - pStreamShared->State.tsLastReadNs) / RT_NS_1MS;
     1665            if (tsDeltaMs >= pStreamShared->State.Cfg.Device.cMsSchedulingHint)
    15711666                fDoRead = true;
    1572                 pStreamShared->State.tsLastUpdateNs = tsNowNs;
    1573             }
    1574         }
    1575         Log3Func(("[SD%RU8] fInTimer=%RTbool, tsLastUpdateNs=%RU64, fDoRead=%RTbool\n",
    1576                   pStreamShared->u8SD, fInTimer, pStreamShared->State.tsLastUpdateNs, fDoRead));
    1577 
     1667
     1668            Log3Func(("tsDeltaMs=%RU64, fDoRead=%RTbool\n", tsDeltaMs, fDoRead));
     1669        }
     1670
     1671        if (fDoRead)
     1672        {
    15781673# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
    1579         if (fDoRead)
    1580         {
     1674            /* Notify the async I/O worker thread that there's work to do. */
    15811675            rc2 = hdaR3StreamAsyncIONotify(pStreamR3);
    15821676            AssertRC(rc2);
    1583         }
    15841677# endif
     1678            /* Update last read timestamp so that we know when to run next. */
     1679            pStreamShared->State.tsLastReadNs = tsNowNs;
     1680        }
    15851681
    15861682# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
     
    15961692                           cbToReadFromStream = DrvAudioHlpBytesAlign(cbToReadFromStream, &pStreamShared->State.Cfg.Props);
    15971693
    1598             Log3Func(("[SD%RU8] cbSinkWritable=%RU32, cbStreamReadable=%RU32\n", pStreamShared->u8SD, cbSinkWritable, cbStreamReadable));
     1694#ifdef LOG_ENABLED
     1695            Assert(tsNowNs >= pStreamShared->State.tsLastReadNs);
     1696            const uint64_t deltaLastReadMs = (tsNowNs - pStreamShared->State.tsLastReadNs) / RT_NS_1MS;
     1697            Log3Func(("[SD%RU8] deltaLastReadMs=%RU64\n",
     1698                      pStreamShared->u8SD, deltaLastReadMs));
     1699#endif
     1700            Log3Func(("[SD%RU8] cbSinkWritable=%RU32, cbStreamReadable=%RU32 -> cbToReadFromStream=%RU32\n",
     1701                      pStreamShared->u8SD, cbSinkWritable, cbStreamReadable, cbToReadFromStream));
    15991702
    16001703            if (cbToReadFromStream)
     
    16661769        {
    16671770# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
    1668             const uint64_t tsNowNs = RTTimeNanoTS();
    1669             if (tsNowNs - pStreamShared->State.tsLastUpdateNs >= pStreamShared->State.Cfg.Device.cMsSchedulingHint * RT_NS_1MS)
     1771            if (tsNowNs - pStreamShared->State.tsLastReadNs >= pStreamShared->State.Cfg.Device.cMsSchedulingHint * RT_NS_1MS)
    16701772            {
    16711773                rc2 = hdaR3StreamAsyncIONotify(pStreamR3);
    16721774                AssertRC(rc2);
    16731775
    1674                 pStreamShared->State.tsLastUpdateNs = tsNowNs;
     1776                pStreamShared->State.tsLastReadNs = tsNowNs;
    16751777            }
    16761778# endif
  • trunk/src/VBox/Devices/Audio/HDAStream.h

    r87569 r87758  
    126126    /** Current BDLE (Buffer Descriptor List Entry). */
    127127    HDABDLE                 BDLE;
    128     /** Timestamp of the last DMA data transfer. */
     128    /** Timestamp (absolute, in timer ticks) of the last DMA data transfer. */
    129129    uint64_t                tsTransferLast;
    130     /** Timestamp of the next DMA data transfer.
     130    /** Timestamp (absolute, in timer ticks) of the next DMA data transfer.
    131131     *  Next for determining the next scheduling window.
    132132     *  Can be 0 if no next transfer is scheduled. */
     
    140140    uint8_t                 cTransferPendingInterrupts;
    141141    uint8_t                 abPadding2[7];
    142     /** The stream's timer Hz rate.
    143      *  This value can can be different from the device's default Hz rate,
    144      *  depending on the rate the stream expects (e.g. for 5.1 speaker setups).
    145      *  Set in hdaR3StreamInit(). */
    146     uint16_t                uTimerHz;
     142    /** The stream's I/O timer Hz rate. */
     143    uint16_t                uTimerIoHz;
    147144    /** Number of audio data frames for the position adjustment.
    148145     *  0 if no position adjustment is needed. */
     
    163160     *  Should match SDFMT. */
    164161    PDMAUDIOSTREAMCFG       Cfg;
    165     /** Timestamp (in ns) of last stream update. */
    166     uint64_t                tsLastUpdateNs;
     162    /** Timestamp (real time, in ns) of last DMA transfer. */
     163    uint64_t                tsLastTransferNs;
     164    /** Timestamp (real time, in ns) of last stream read (to backends).
     165     *  When running in async I/O mode, this differs from \a tsLastTransferNs,
     166     *  because reading / processing will be done in a separate stream. */
     167    uint64_t                tsLastReadNs;
    167168} HDASTREAMSTATE;
    168169AssertCompileSizeAlignment(HDASTREAMSTATE, 8);
  • trunk/src/VBox/Devices/Audio/HDAStreamPeriod.cpp

    r82968 r87758  
    109109        cTotalPeriods = 2; /* At least two periods *must* be present (LVI >= 1). */
    110110
    111     uint32_t cFramesToTransfer = (u32CBL / 4 /** @todo Define frame size? */) / cTotalPeriods;
    112 
    113     pPeriod->u8SD              = u8SD;
    114     pPeriod->u64StartWalClk    = 0;
    115     pPeriod->u32Hz             = pStreamCfg->Props.uHz;
    116     pPeriod->u64DurationWalClk = hdaR3StreamPeriodFramesToWalClk(pPeriod, cFramesToTransfer);
    117     pPeriod->u64ElapsedWalClk  = 0;
    118     pPeriod->i64DelayWalClk    = 0;
     111    uint32_t cFramesToTransfer =
     112        (u32CBL / (pStreamCfg->Props.cbSample * pStreamCfg->Props.cChannels /* Frame size */)) / cTotalPeriods;
     113
     114    pPeriod->u8SD               = u8SD;
     115    pPeriod->u64StartWalClk     = 0;
     116    pPeriod->u32Hz              = pStreamCfg->Props.uHz;
     117    pPeriod->u64DurationWalClk  = hdaR3StreamPeriodFramesToWalClk(pPeriod, cFramesToTransfer);
     118    pPeriod->u64ElapsedWalClk   = 0;
     119    pPeriod->i64DelayWalClk     = 0;
    119120    pPeriod->cFramesToTransfer  = cFramesToTransfer;
    120121    pPeriod->cFramesTransferred = 0;
    121     pPeriod->cIntPending       = 0;
     122    pPeriod->cIntPending        = 0;
    122123
    123124    Log3Func(("[SD%RU8] %RU64 long, Hz=%RU32, CBL=%RU32, LVI=%RU16 -> %u periods, %RU32 frames each\n",
Note: See TracChangeset for help on using the changeset viewer.

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