VirtualBox

Changeset 73370 in vbox


Ignore:
Timestamp:
Jul 26, 2018 1:52:12 PM (7 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
123987
Message:

Audio: Implemented backend-independent (pre-)buffering support. Work in progress.

Location:
trunk
Files:
21 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/vmm/pdmaudioifs.h

    r73242 r73370  
    44
    55/*
    6  * Copyright (C) 2006-2017 Oracle Corporation
     6 * Copyright (C) 2006-2018 Oracle Corporation
    77 *
    88 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    8282 * A PDMAUDIOFRAME is the internal representation of a single audio frame, which consists of a single left
    8383 * and right audio sample in time. Only mono (1) and stereo (2) channel(s) currently are supported.
     84 *
     85 *
     86 * == Timing
     87 *
     88 * Handling audio data in a virtual environment is hard, as the human perception is very sensitive
     89 * to the slightest cracks and stutters in the audible data. This can happen if the VM's timing is
     90 * lagging behind or not within the expected time frame.
     91 *
     92 * The two main components which unfortunately contradict each other is a) the audio device emulation
     93 * and b) the audio backend(s) on the host. Those need to be served in a timely manner to function correctly.
     94 * To make e.g. the device emulation rely on the pace the host backend(s) set - or vice versa - will not work,
     95 * as the guest's audio system / drivers then will not be able to compensate this accordingly.
     96 *
     97 * So each component, the device emulation, the audio connector(s) and the backend(s) must do its thing
     98 * *when* it needs to do it, independently of the others. For that we use various (small) ring buffers to
     99 * (hopefully) serve all components with the amount of data *when* they need it.
     100 *
     101 * Additionally, the device emulation can run with a different audio frame size, while the backends(s) may
     102 * require a different frame size (16 bit stereo -> 8 bit mono, for example).
     103 *
     104 * The device emulation can give the audio connector(s) a scheduling hint (optional), e.g. in which interval
     105 * it expects any data processing.
     106 *
     107 * A data transfer for playing audio data from the guest on the host looks like this:
     108 * (RB = Ring Buffer, MB = Mixing Buffer)
     109 *
     110 * (A) Device DMA -> (B) Device RB -> (C) Audio Connector Guest MB -> (D) Audio Connector Host MB -> \
     111 * (E) Backend RB (optional, up to the backend) > (F) Backend audio framework
     112 *
     113 * For capturing audio data the above chain is similar, just in a different direction, of course.
     114 *
     115 * The audio connector hereby plays a key role when it comes to (pre-) buffering data to minimize any audio stutters
     116 * and/or cracks. The following values, which also can be tweaked via CFGM / extra-data are available:
     117 *
     118 * - The pre-buffering time (in ms): Audio data which needs to be buffered before any playback (or capturing) can happen.
     119 * - The actual buffer size (in ms): How big the mixing buffer (for C and D) will be.
     120 * - The period size (in ms): How big a chunk of audio (often called period or fragment) for F must be to get handled correctly.
     121 *
     122 * The above values can be set on a per-driver level, whereas input and output streams for a driver also can be handled
     123 * set independently. The verbose audio (release) log will tell about the (final) state of each audio stream.
    84124 *
    85125 *
     
    535575     *      The audio data will get handled as PDMAUDIOFRAME frames without any modification done. */
    536576    PDMAUDIOSTREAMLAYOUT     enmLayout;
    537     /** Hint about the optimal frame buffer size (in audio frames).
    538      *  0 if no hint is given. */
    539     uint32_t                 cFrameBufferHint;
     577    /** Device emulation-specific data needed for the audio connector. */
    540578    struct
    541579    {
    542         /** Scheduling hint given from the device emulation about when this stream is being served on average.
     580        /** Scheduling hint set by the device emulation about when this stream is being served on average (in ms).
    543581         *  Can be 0 if not hint given or some other mechanism (e.g. callbacks) is being used. */
    544582        uint32_t             uSchedulingHintMs;
     
    546584    /**
    547585     * Backend-specific data for the stream.
     586     * On input (requested configuration) those values are set by the audio connector to let the backend know what we expect.
     587     * On output (acquired configuration) those values reflect the values set and used by the backend.
    548588     * Set by the backend on return. Not all backends support all values / features.
    549589     */
     
    9991039    PDMAUDIOSTREAMCTX      enmCtx;
    10001040    /** Timestamp (in ms) since last iteration. */
    1001     uint64_t               tsLastIterateMS;
     1041    uint64_t               tsLastIteratedMs;
     1042    /** Timestamp (in ms) since last playback / capture. */
     1043    uint64_t               tsLastPlayedCapturedMs;
     1044    /** Timestamp (in ms) since last read (input streams) or
     1045     *  write (output streams). */
     1046    uint64_t               tsLastReadWrittenMs;
     1047    /** For output streams this indicates whether the stream has reached
     1048     *  its playback threshold, e.g. is playing audio.
     1049     *  For input streams this  indicates whether the stream has enough input
     1050     *  data to actually start reading audio. */
     1051    bool                   fThresholdReached;
    10021052    /** Union for input/output specifics. */
    10031053    union
  • trunk/src/VBox/Devices/Audio/AudioMixer.cpp

    r73342 r73370  
    853853    uint32_t cbReadable = 0;
    854854
     855    if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
     856    {
    855857#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
    856858# error "Implement me!"
    857859#else
    858     /* The hosts sets the pace --
    859      * so we try to find the maximum of readable data of all connected streams to this sink. */
    860     PAUDMIXSTREAM pMixStream;
    861     RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
    862     {
    863         if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
    864         {
    865             Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName));
    866             continue;
    867         }
    868 
    869         cbReadable = RT_MAX(cbReadable,
    870                             pMixStream->pConn->pfnStreamGetReadable(pMixStream->pConn, pMixStream->pStream));
    871 
    872         break; /** @todo For now we only support recording by the first stream added. */
    873     }
     860        /* Return how much data we can deliver since the last read. */
     861        cbReadable = DrvAudioHlpMsToBytes(&pSink->PCMProps, RTTimeMilliTS() - pSink->tsLastReadWrittenMs);
    874862#endif
     863    }
    875864
    876865    Log3Func(("[%s] cbReadable=%RU32\n", pSink->pszName, cbReadable));
     
    899888        return 0;
    900889
    901     uint32_t cbWritable = UINT32_MAX;
     890    uint32_t cbWritable = 0;
    902891
    903892    if (    (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
     
    907896# error "Implement me!"
    908897#else
    909         /* The hosts sets the pace --
    910          * so we try to find the minimum of writable data to all connected streams to this sink. */
    911         PAUDMIXSTREAM pMixStream;
    912         RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
    913         {
    914             const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
    915 
    916             Log3Func(("[%s] Stream '%s' cbWritableStream=%RU32\n", pSink->pszName, pMixStream->pszName, cbWritableStream));
    917 
    918             if (cbWritableStream < cbWritable)
    919                 cbWritable = cbWritableStream;
    920         }
     898        /* Return how much data we expect since the last write. */
     899        cbWritable = DrvAudioHlpMsToBytes(&pSink->PCMProps, RTTimeMilliTS() - pSink->tsLastReadWrittenMs);
    921900#endif
    922901    }
    923 
    924     if (cbWritable == UINT32_MAX)
    925         cbWritable = 0;
    926902
    927903    Log3Func(("[%s] cbWritable=%RU32\n", pSink->pszName, cbWritable));
     
    11671143            pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
    11681144
     1145        /* Update our last read time stamp. */
     1146        pSink->tsLastReadWrittenMs = RTTimeMilliTS();
     1147
    11691148#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
    11701149# error "Implement me!"
     
    13041283
    13051284    /* Update last updated timestamp. */
    1306     pSink->tsLastUpdatedMS = RTTimeMilliTS();
     1285    pSink->tsLastUpdatedMs = RTTimeMilliTS();
    13071286
    13081287    /* Reset status. */
     
    15811560
    15821561    /* Update last updated timestamp. */
    1583     pSink->tsLastUpdatedMS = RTTimeMilliTS();
     1562    pSink->tsLastUpdatedMs = RTTimeMilliTS();
    15841563
    15851564    /* All streams disabled and the sink is in pending disable mode? */
     
    17581737    }
    17591738
     1739    /* Update our last written time stamp. */
     1740    pSink->tsLastReadWrittenMs = RTTimeMilliTS();
     1741
    17601742    if (pcbWritten)
    17611743        *pcbWritten = cbBuf; /* Always report everything written, as the backends need to keep up themselves. */
  • trunk/src/VBox/Devices/Audio/AudioMixer.h

    r73356 r73370  
    196196    /** The volume of this sink, combined with the last set  master volume. */
    197197    PDMAUDIOVOLUME          VolumeCombined;
    198     /** Timestamp (in ms) since last update. */
    199     uint64_t                tsLastUpdatedMS;
     198    /** Timestamp since last update (in ms). */
     199    uint64_t                tsLastUpdatedMs;
     200    /** Last read (recording) / written (playback) timestamp (in ms). */
     201    uint64_t                tsLastReadWrittenMs;
    200202#ifdef VBOX_AUDIO_MIXER_DEBUG
    201203    struct
  • trunk/src/VBox/Devices/Audio/DevIchAc97.cpp

    r73242 r73370  
    313313    /** The stream's current configuration. */
    314314    PDMAUDIOSTREAMCFG     Cfg; //+104
     315    uint32_t              Padding2;
    315316#ifdef VBOX_WITH_AUDIO_AC97_ASYNC_IO
    316317    /** Asynchronous I/O state members. */
  • trunk/src/VBox/Devices/Audio/DevSB16.cpp

    r73241 r73370  
    16051605        pThis->left_till_irq = pThis->block_size;
    16061606
    1607     uint32_t cbOutMin = UINT32_MAX;
    1608 
    1609     PSB16DRIVER pDrv;
    1610     RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
    1611     {
    1612         if (!pDrv->Out.pStream)
    1613             continue;
    1614 
    1615         uint32_t cbOut = pDrv->pConnector->pfnStreamGetWritable(pDrv->pConnector, pDrv->Out.pStream);
    1616 
    1617         if (cbOut < cbOutMin)
    1618             cbOutMin = cbOut;
    1619     }
    1620 
    1621     LogFlowFunc(("cbOutMin=%RU32\n", cbOutMin));
    1622     if (cbOutMin == UINT32_MAX)
    1623     {
    1624         free = dma_len;
    1625     }
    1626     else
    1627     {
    1628         free = cbOutMin & ~pThis->align; /** @todo int vs. uint32. */
    1629         if ((free <= 0) || !dma_len)
    1630             return dma_pos;
    1631     }
     1607    free = dma_len;
    16321608
    16331609    copy = free;
  • trunk/src/VBox/Devices/Audio/DrvAudio.cpp

    r73230 r73370  
    560560        }
    561561
     562        case PDMAUDIOSTREAMCMD_DRAIN:
     563        {
     564            rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream->pvBackend, PDMAUDIOSTREAMCMD_DRAIN);
     565            break;
     566        }
     567
    562568        default:
    563569        {
     
    570576    if (RT_FAILURE(rc))
    571577    {
    572         LogRel(("Audio: %s stream '%s' failed with %Rrc\n", DrvAudioHlpStreamCmdToStr(enmStreamCmd), pHstStream->szName, rc));
     578        if (   rc != VERR_NOT_IMPLEMENTED
     579            && rc != VERR_NOT_SUPPORTED)
     580            LogRel(("Audio: %s stream '%s' failed with %Rrc\n", DrvAudioHlpStreamCmdToStr(enmStreamCmd), pHstStream->szName, rc));
     581
    573582        LogFunc(("[%s] %s failed with %Rrc\n", pStream->szName, DrvAudioHlpStreamCmdToStr(enmStreamCmd), rc));
    574583    }
     
    598607    AssertPtr(pGstStream);
    599608
     609/*    pCfgHost->Backend.cfPreBuf = DrvAudioHlpMsToFrames(&pCfgHost->Props,
     610                                                       pCfgGuest->enmDir == PDMAUDIODIR_IN
     611                                                       ? pThis->In.msLatencyHost : pThis->Out.msLatencyHost);*/
     612    /* Latency can be 0 if not configured. */
     613
    600614    /*
    601615     * Init host stream.
     
    606620
    607621#ifdef DEBUG
    608     LogFunc(("[%s] Requested host format:\n", pStream->szName));
     622    LogFunc(("[%s] Requested host format:\n", pHstStream->szName));
    609623    DrvAudioHlpStreamCfgPrint(pCfgHost);
    610 #else
    611     LogRel2(("Audio: Creating stream '%s'\n", pStream->szName));
    612     LogRel2(("Audio: Requested %s host format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
    613              pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName,
     624#endif
     625
     626    LogRel2(("Audio: Creating stream '%s'\n", pHstStream->szName));
     627    LogRel2(("Audio: Guest %s format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
     628             pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pGstStream->szName,
     629             pCfgGuest->Props.uHz, pCfgGuest->Props.cBits, pCfgGuest->Props.fSigned ? "S" : "U",
     630             pCfgGuest->Props.cChannels, pCfgGuest->Props.cChannels == 1 ? "Channel" : "Channels"));
     631    LogRel2(("Audio: Requested host %s format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
     632             pCfgHost->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pHstStream->szName,
    614633             pCfgHost->Props.uHz, pCfgHost->Props.cBits, pCfgHost->Props.fSigned ? "S" : "U",
    615              pCfgHost->Props.cChannels, pCfgHost->Props.cChannels == 0 ? "Channel" : "Channels"));
    616 #endif
     634             pCfgHost->Props.cChannels, pCfgHost->Props.cChannels == 1 ? "Channel" : "Channels"));
    617635
    618636    PDMAUDIOSTREAMCFG CfgHostAcq;
     
    622640
    623641#ifdef DEBUG
    624     LogFunc(("[%s] Acquired host format:\n",  pStream->szName));
     642    LogFunc(("[%s] Acquired host format:\n",  pHstStream->szName));
    625643    DrvAudioHlpStreamCfgPrint(&CfgHostAcq);
    626 #else
    627     LogRel2(("Audio: Acquired %s host format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
     644#endif
     645
     646    LogRel2(("Audio: Acquired host %s format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
    628647             CfgHostAcq.enmDir == PDMAUDIODIR_IN ? "recording" : "playback",  pStream->szName,
    629648             CfgHostAcq.Props.uHz, CfgHostAcq.Props.cBits, CfgHostAcq.Props.fSigned ? "S" : "U",
    630              CfgHostAcq.Props.cChannels, CfgHostAcq.Props.cChannels == 0 ? "Channel" : "Channels"));
    631 #endif
    632 
    633     /* No frame buffer size hint given by the backend? Default to some sane value. */
    634     if (!CfgHostAcq.cFrameBufferHint)
    635     {
    636         CfgHostAcq.cFrameBufferHint = _1K; /** @todo Make this configurable? */
    637     }
     649             CfgHostAcq.Props.cChannels, CfgHostAcq.Props.cChannels == 1 ? "Channel" : "Channels"));
     650
     651    /* Let the user know if the backend changed some of the tweakable values. */
     652    if (CfgHostAcq.Backend.cfBufferSize != pCfgHost->Backend.cfBufferSize)
     653        LogRel2(("Audio: Backend changed buffer size from %RU32 to %RU32 frames\n",
     654                 pCfgHost->Backend.cfBufferSize, CfgHostAcq.Backend.cfBufferSize));
     655
     656    if (CfgHostAcq.Backend.cfPeriod != pCfgHost->Backend.cfPeriod)
     657        LogRel2(("Audio: Backend changed period size from %RU32 to %RU32 frames\n",
     658                 pCfgHost->Backend.cfPeriod, CfgHostAcq.Backend.cfPeriod));
     659
     660    if (CfgHostAcq.Backend.cfPreBuf != pCfgHost->Backend.cfPreBuf)
     661        LogRel2(("Audio: Backend changed pre-buffering size from %RU32 to %RU32 frames\n",
     662                 pCfgHost->Backend.cfPreBuf, CfgHostAcq.Backend.cfPreBuf));
     663
     664    /*
     665     * Configure host buffers.
     666     */
     667
     668    /* If no own pre-buffer is set, let the backend choose. */
     669    uint32_t msPreBuf = DrvAudioHlpFramesToMs(&pCfgHost->Props, CfgHostAcq.Backend.cfPreBuf);
     670    LogRel2(("Audio: Pre-buffering size of stream '%s' is %RU32ms (%RU32 frames)\n",
     671             pHstStream->szName, msPreBuf, CfgHostAcq.Backend.cfPreBuf));
     672
     673    /* Make sure the configured buffer size by the backend at least can hold the configured latency. */
     674    const uint32_t msPeriod = DrvAudioHlpFramesToMs(&pCfgHost->Props, CfgHostAcq.Backend.cfPeriod);
     675
     676    LogRel2(("Audio: Period size of stream '%s' is %RU32ms (%RU32 frames)\n",
     677             pHstStream->szName, msPeriod, CfgHostAcq.Backend.cfPeriod));
     678
     679    /* Check if the backend did return sane values and correct if necessary. */
     680    if (CfgHostAcq.Backend.cfBufferSize < CfgHostAcq.Backend.cfPeriod)
     681    {
     682        LogRel2(("Audio: Warning: Backend of stream '%s' did not set a valid buffer size (%RU32 frames), setting to %RU32 frames",
     683                 pHstStream->szName, CfgHostAcq.Backend.cfBufferSize, CfgHostAcq.Backend.cfPeriod));
     684        AssertFailed(); /* Should never happen. */
     685    }
     686
     687    uint32_t msBufferSize = DrvAudioHlpFramesToMs(&pCfgHost->Props, CfgHostAcq.Backend.cfBufferSize);
     688
     689    LogRel2(("Audio: Buffer size of stream '%s' is %RU32ms (%RU32 frames)\n",
     690             pHstStream->szName, msBufferSize, CfgHostAcq.Backend.cfBufferSize));
     691
     692    /* Set set host buffer size multiplicator. */
     693    const unsigned cHstBufferFactor = 2; /** @todo Make this configurable. */
    638694
    639695    /* Destroy any former mixing buffer. */
     
    643699    CfgHostAcq.Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(CfgHostAcq.Props.cBits, CfgHostAcq.Props.cChannels);
    644700
    645     /* Set set host buffer size multiplicator. */
    646     const unsigned cFrameBufferHostFactor = 2; /** @todo Make this configurable. */
    647 
    648     LogFunc(("[%s] cFrames=%RU32 (x %u)\n", pHstStream->szName, CfgHostAcq.cFrameBufferHint, cFrameBufferHostFactor));
    649 
    650701    int rc2 = AudioMixBufInit(&pHstStream->MixBuf, pHstStream->szName, &CfgHostAcq.Props,
    651                               CfgHostAcq.cFrameBufferHint * cFrameBufferHostFactor);
     702                              CfgHostAcq.Backend.cfBufferSize * cHstBufferFactor);
    652703    AssertRC(rc2);
    653704
     
    660711     */
    661712
     713    if (pCfgGuest->Device.uSchedulingHintMs)
     714        LogRel2(("Audio: Stream '%s' got a scheduling hint of %RU32ms\n", pGstStream->szName, pCfgGuest->Device.uSchedulingHintMs));
     715
    662716    /* Destroy any former mixing buffer. */
    663717    AudioMixBufDestroy(&pGstStream->MixBuf);
    664718
    665719    /* Set the guests's default audio data layout. */
    666     pCfgHost->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
     720    pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
    667721
    668722    /* Make sure to (re-)set the guest buffer's shift size. */
     
    670724
    671725    /* Set set guest buffer size multiplicator. */
    672     const unsigned cFrameBufferGuestFactor = 10; /** @todo Make this configurable. */
    673 
    674     LogFunc(("[%s] cFrames=%RU32 (x %u)\n", pGstStream->szName, CfgHostAcq.cFrameBufferHint, cFrameBufferGuestFactor));
     726    const unsigned cGstBufferFactor = 4; /** @todo Make this configurable. */
    675727
    676728    rc2 = AudioMixBufInit(&pGstStream->MixBuf, pGstStream->szName, &pCfgGuest->Props,
    677                           CfgHostAcq.cFrameBufferHint * cFrameBufferGuestFactor);
     729                          CfgHostAcq.Backend.cfBufferSize * cGstBufferFactor);
    678730    AssertRC(rc2);
    679731
     
    938990        }
    939991
    940         const uint32_t cbFree = AudioMixBufFreeBytes(&pGstStream->MixBuf);
    941         if (!cbFree) /* No free guest side buffer space, bail out. */
    942         {
    943             AssertMsgFailed(("[%s] Stream full\n", pGstStream->szName));
     992        const uint32_t cbFree = AudioMixBufFreeBytes(&pHstStream->MixBuf);
     993        if (cbFree < cbBuf) /* No space left on host side? Bail out. */
     994            LogRel2(("Audio: Lost audio frames (%RU32) due to full host stream '%s', expect stuttering audio output\n",
     995                     DrvAudioHlpBytesToFrames(&pHstStream->Cfg.Props, cbBuf - cbFree), pHstStream->szName));
     996
     997        uint32_t cbToWrite = RT_MIN(cbBuf, cbFree);
     998        if (cbToWrite > cbBuf) /* Paranoia. */
     999            cbToWrite = cbBuf;
     1000
     1001        if (!cbToWrite)
    9441002            break;
    945         }
    946 
    947         /* Do not attempt to write more than the guest side currently can handle. */
    948         if (cbBuf > cbFree)
    949             cbBuf = cbFree;
     1003
     1004        uint64_t tsDeltaWrittenMs = RTTimeMilliTS() - pHstStream->tsLastReadWrittenMs;
     1005
     1006        Log3Func(("[%s] fThresholdReached=%RTbool, tsDeltaWrittenMs=%RU64\n",
     1007                  pHstStream->szName, pHstStream->fThresholdReached, tsDeltaWrittenMs));
     1008
     1009        if (   pHstStream->fThresholdReached
     1010            && tsDeltaWrittenMs < pGstStream->Cfg.Device.uSchedulingHintMs)
     1011        {
     1012            break;
     1013        }
    9501014
    9511015        /* We use the guest side mixing buffer as an intermediate buffer to do some
    9521016         * (first) processing (if needed), so always write the incoming data at offset 0. */
    953         uint32_t cfWritten = 0;
    954         rc = AudioMixBufWriteAt(&pGstStream->MixBuf, 0 /* offFrames */, pvBuf, cbBuf, &cfWritten);
     1017        uint32_t cfGstWritten = 0;
     1018        rc = AudioMixBufWriteAt(&pGstStream->MixBuf, 0 /* offFrames */, pvBuf, cbToWrite, &cfGstWritten);
    9551019        if (   RT_FAILURE(rc)
    956             || !cfWritten)
    957         {
    958             AssertMsgFailed(("[%s] Write failed: cbBuf=%RU32, cfWritten=%RU32, rc=%Rrc\n",
    959                              pGstStream->szName, cbBuf, cfWritten, rc));
     1020            || !cfGstWritten)
     1021        {
     1022            AssertMsgFailed(("[%s] Write failed: cbToWrite=%RU32, cfWritten=%RU32, rc=%Rrc\n",
     1023                             pGstStream->szName, cbToWrite, cfGstWritten, rc));
    9601024            break;
    9611025        }
    9621026
    9631027        if (pThis->Out.Cfg.Dbg.fEnabled)
    964             DrvAudioHlpFileWrite(pHstStream->Out.Dbg.pFileStreamWrite, pvBuf, cbBuf, 0 /* fFlags */);
     1028            DrvAudioHlpFileWrite(pHstStream->Out.Dbg.pFileStreamWrite, pvBuf, cbToWrite, 0 /* fFlags */);
    9651029
    9661030#ifdef VBOX_WITH_STATISTICS
    967         STAM_COUNTER_ADD(&pThis->Stats.TotalFramesWritten, cfWritten);
     1031        STAM_COUNTER_ADD(&pThis->Stats.TotalFramesWritten, cfGstWritten);
    9681032#endif
    969         uint32_t cfMixed = 0;
    970         if (cfWritten)
    971         {
    972             int rc2 = AudioMixBufMixToParentEx(&pGstStream->MixBuf, 0 /* cSrcOffset */, cfWritten /* cSrcFrames */,
    973                                                &cfMixed /* pcSrcMixed */);
    974             if (   RT_FAILURE(rc2)
    975                 || cfMixed < cfWritten)
     1033        uint32_t cfGstMixed = 0;
     1034        if (cfGstWritten)
     1035        {
     1036            int rc2 = AudioMixBufMixToParentEx(&pGstStream->MixBuf, 0 /* cSrcOffset */, cfGstWritten /* cSrcFrames */,
     1037                                               &cfGstMixed /* pcSrcMixed */);
     1038            if (RT_FAILURE(rc2))
    9761039            {
    977                 AssertMsgFailed(("[%s] Mixing failed: cbBuf=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n",
    978                                  pGstStream->szName, cbBuf, cfWritten, cfMixed, rc2));
    979 
    980                 LogRel2(("Audio: Lost audio frames (%RU32) due to full host stream '%s', expect stuttering audio output\n",
    981                          cfWritten - cfMixed, pHstStream->szName));
     1040                AssertMsgFailed(("[%s] Mixing failed: cbToWrite=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n",
     1041                                 pGstStream->szName, cbToWrite, cfGstWritten, cfGstMixed, rc2));
     1042            }
     1043            else
     1044            {
     1045                Log3Func(("[%s] Buffer: Last written %RU64ms, writing %RU32 frames (%RU64ms), now filled with %RU64ms -- %RU8%%\n",
     1046                          pHstStream->szName, tsDeltaWrittenMs, cfGstWritten, DrvAudioHlpFramesToMs(&pHstStream->Cfg.Props, cfGstWritten),
     1047                          DrvAudioHlpFramesToMs(&pHstStream->Cfg.Props, AudioMixBufUsed(&pHstStream->MixBuf)),
     1048                          AudioMixBufUsed(&pHstStream->MixBuf) * 100 / AudioMixBufSize(&pHstStream->MixBuf)));
     1049
     1050                pHstStream->tsLastReadWrittenMs = RTTimeMilliTS();
    9821051
    9831052                /* Keep going. */
     
    9871056                rc = rc2;
    9881057
    989             cbWrittenTotal = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cfWritten);
     1058            cbWrittenTotal = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cfGstWritten);
    9901059
    9911060#ifdef VBOX_WITH_STATISTICS
    992             STAM_COUNTER_ADD(&pThis->Stats.TotalFramesMixedOut,      cfMixed);
    993             Assert(cfWritten >= cfMixed);
    994             STAM_COUNTER_ADD(&pThis->Stats.TotalFramesLostOut,       cfWritten - cfMixed);
     1061            STAM_COUNTER_ADD(&pThis->Stats.TotalFramesMixedOut,      cfGstMixed);
     1062            Assert(cfGstWritten >= cfGstMixed);
     1063            STAM_COUNTER_ADD(&pThis->Stats.TotalFramesLostOut,       cfGstWritten - cfGstMixed);
    9951064            STAM_COUNTER_ADD(&pThis->Stats.TotalBytesWritten,        cbWrittenTotal);
    9961065            STAM_COUNTER_ADD(&pGstStream->Out.StatBytesTotalWritten, cbWrittenTotal);
     
    9981067        }
    9991068
    1000         Log3Func(("[%s] cbBuf=%RU32, cUsed=%RU32, cLive=%RU32, cWritten=%RU32, cMixed=%RU32, rc=%Rrc\n",
    1001                   pGstStream->szName,
    1002                   cbBuf, AudioMixBufUsed(&pGstStream->MixBuf), AudioMixBufLive(&pGstStream->MixBuf), cfWritten, cfMixed, rc));
     1069        Log3Func(("[%s] cbToWrite=%RU32, cfUsed=%RU32, cfLive=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n",
     1070                  pGstStream->szName, cbToWrite, AudioMixBufUsed(&pGstStream->MixBuf),
     1071                  AudioMixBufLive(&pGstStream->MixBuf), cfGstWritten, cfGstMixed, rc));
    10031072
    10041073    } while (0);
     
    11891258            /* No audio frames to transfer from guest to host (anymore)?
    11901259             * Then try closing this stream if marked so in the next block. */
    1191             fTryClosePending = AudioMixBufLive(&pHstStream->MixBuf) == 0;
     1260            const uint32_t cfLive = AudioMixBufLive(&pHstStream->MixBuf);
     1261            fTryClosePending = cfLive == 0;
     1262            Log3Func(("[%s] fTryClosePending=%RTbool, cfLive=%RU32\n", pHstStream->szName, fTryClosePending, cfLive));
    11921263        }
    11931264        else
     
    11991270            && fTryClosePending)
    12001271        {
    1201             if (pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional to implement. */
     1272            /* Tell the backend to drain the stream, that is, play the remaining (buffered) data
     1273             * on the backend side. */
     1274            rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DRAIN);
     1275            if (rc == VERR_NOT_SUPPORTED) /* Not all backends support draining. */
     1276                rc = VINF_SUCCESS;
     1277
     1278            if (RT_SUCCESS(rc))
    12021279            {
    1203                 const uint32_t cxPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pHstStream->pvBackend);
    1204                 Log3Func(("[%s] cxPending=%RU32\n", pHstStream->szName, cxPending));
    1205 
    1206                 /* Only try close pending if no audio data is pending on the backend-side anymore. */
    1207                 fTryClosePending = cxPending == 0;
     1280                if (pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional to implement. */
     1281                {
     1282                    const uint32_t cxPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pHstStream->pvBackend);
     1283                    Log3Func(("[%s] cxPending=%RU32\n", pHstStream->szName, cxPending));
     1284
     1285                    /* Only try close pending if no audio data is pending on the backend-side anymore. */
     1286                    fTryClosePending = cxPending == 0;
     1287                }
     1288
     1289                if (fTryClosePending)
     1290                {
     1291                    LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
     1292                    rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
     1293                    if (RT_FAILURE(rc))
     1294                        LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pHstStream->szName, rc));
     1295                }
    12081296            }
    1209 
    1210             if (fTryClosePending)
    1211             {
    1212                 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
    1213                 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
    1214                 if (RT_FAILURE(rc))
    1215                     LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pHstStream->szName, rc));
    1216             }
    12171297        }
    12181298
     
    12201300
    12211301    /* Update timestamps. */
    1222     pHstStream->tsLastIterateMS = RTTimeMilliTS();
    1223     pGstStream->tsLastIterateMS = RTTimeMilliTS();
     1302    pGstStream->tsLastIteratedMs = RTTimeMilliTS();
    12241303
    12251304    if (RT_FAILURE(rc))
     
    14771556    uint32_t cfPlayedTotal = 0;
    14781557
     1558    if (!pThis->pHostDrvAudio)
     1559        return VINF_SUCCESS;
     1560
     1561    PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
     1562    AssertPtr(pHstStream);
     1563    PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
     1564    AssertPtr(pGstStream);
     1565
     1566    AssertReleaseMsgReturn(pHstStream != NULL,
     1567                              ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
     1568                               pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
     1569                              VERR_NOT_AVAILABLE);
     1570    AssertReleaseMsgReturn(pGstStream != NULL,
     1571                            ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
     1572                               pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
     1573                               VERR_NOT_AVAILABLE);
     1574
     1575    PDMAUDIOSTREAMSTS stsHstStream = pHstStream->fStatus;
     1576#ifdef LOG_ENABLED
     1577    char *pszStreamSts = dbgAudioStreamStatusToStr(stsHstStream);
     1578    Log3Func(("[%s] Start: stsHstStream=%s\n", pHstStream->szName, pszStreamSts));
     1579    RTStrFree(pszStreamSts);
     1580#endif /* LOG_ENABLED */
     1581
    14791582    do
    14801583    {
    1481         if (!pThis->pHostDrvAudio)
    1482             break;
    1483 
    1484         PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
    1485         AssertPtr(pHstStream);
    1486         PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
    1487         AssertPtr(pGstStream);
    1488 
    1489         AssertReleaseMsgBreakStmt(pHstStream != NULL,
    1490                                   ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
    1491                                    pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
    1492                                   rc = VERR_NOT_AVAILABLE);
    1493         AssertReleaseMsgBreakStmt(pGstStream != NULL,
    1494                                   ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
    1495                                    pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
    1496                                   rc = VERR_NOT_AVAILABLE);
    1497 
    14981584        /*
    14991585         * Check if the backend is ready to operate.
    15001586         */
    1501 
    1502         AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
    1503         PDMAUDIOSTREAMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend);
     1587        if (!(stsHstStream & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Stream disabled? Bail out. */
     1588            break;
     1589
     1590              uint32_t cfLive       = AudioMixBufLive(&pHstStream->MixBuf);
     1591        const uint8_t  uLivePercent = (100 * cfLive) / AudioMixBufSize(&pHstStream->MixBuf);
     1592
     1593        if (!pHstStream->tsLastPlayedCapturedMs)
     1594            pHstStream->tsLastPlayedCapturedMs = RTTimeMilliTS();
     1595        uint64_t tsDeltaPlayedCapturedMs = RTTimeMilliTS() - pHstStream->tsLastPlayedCapturedMs;
     1596        pHstStream->tsLastPlayedCapturedMs = RTTimeMilliTS();
     1597
    15041598#ifdef LOG_ENABLED
    1505         char *pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
    1506         Log3Func(("[%s] Start: stsBackend=%s\n", pHstStream->szName, pszBackendSts));
    1507         RTStrFree(pszBackendSts);
    1508 #endif /* LOG_ENABLED */
    1509         if (!(stsBackend & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Backend disabled? Bail out. */
    1510             break;
    1511 
    1512         uint32_t cfToPlay = AudioMixBufLive(&pHstStream->MixBuf);
    1513 
    1514         if (pThis->pHostDrvAudio->pfnStreamPlayBegin)
    1515             pThis->pHostDrvAudio->pfnStreamPlayBegin(pThis->pHostDrvAudio, pHstStream->pvBackend);
    1516 
    1517         if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
    1518         {
    1519             rc = drvAudioStreamPlayNonInterleaved(pThis, pHstStream, cfToPlay, &cfPlayedTotal);
    1520         }
    1521         else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
    1522         {
    1523             rc = drvAudioStreamPlayRaw(pThis, pHstStream, cfToPlay, &cfPlayedTotal);
    1524         }
    1525         else
    1526             AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
    1527 
    1528         if (pThis->pHostDrvAudio->pfnStreamPlayEnd)
    1529             pThis->pHostDrvAudio->pfnStreamPlayEnd(pThis->pHostDrvAudio, pHstStream->pvBackend);
    1530 
    1531 #ifdef LOG_ENABLED
    1532         uint32_t cfLive = 0;
     1599        Log3Func(("[%s] Buffer: Last played %RU64ms, filled with %RU64ms (%RU8%%) total, "
     1600                  "(cfLive=%RU32, fThresholdReached=%RTbool)\n",
     1601                  pHstStream->szName, tsDeltaPlayedCapturedMs, DrvAudioHlpFramesToMs(&pHstStream->Cfg.Props, cfLive),
     1602                  uLivePercent, cfLive, pHstStream->fThresholdReached));
    15331603#endif
     1604        bool fDoPlay      = pHstStream->fThresholdReached;
     1605        bool fJustStarted = false;
     1606        if (!fDoPlay)
     1607        {
     1608            /* Did we reach the backend's playback (pre-buffering) threshold? Can be 0 if no threshold set. */
     1609            if (cfLive >= pHstStream->Cfg.Backend.cfPreBuf)
     1610            {
     1611                LogRel2(("Audio: Stream '%s' buffering complete\n", pHstStream->szName));
     1612                fDoPlay = true;
     1613            }
     1614
     1615            /* Some audio files are shorter than the pre-buffering level (e.g. the "click" Explorer sounds on some Windows guests),
     1616             * so make sure that we also play those by checking if the stream already is pending disable mode, even if we didn't
     1617             * hit the pre-buffering watermark yet.
     1618             *
     1619             * To reproduce, use "Windows Navigation Start.wav" on Windows 7 (2824 samples). */
     1620            if (   cfLive
     1621                && pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE)
     1622            {
     1623                LogRel2(("Audio: Stream '%s' buffering complete (short sound)\n", pHstStream->szName));
     1624                fDoPlay = true;
     1625            }
     1626
     1627            if (fDoPlay)
     1628            {
     1629                pHstStream->fThresholdReached = true;
     1630                fJustStarted = true;
     1631                LogRel2(("Audio: Stream '%s' started playing\n", pHstStream->szName));
     1632            }
     1633            else /* Not yet, so still buffering audio data. */
     1634                LogRel2(("Audio: Stream '%s' is buffering (%RU8%% complete)\n",
     1635                         pHstStream->szName, (100 * cfLive) / pHstStream->Cfg.Backend.cfPreBuf));
     1636        }
     1637
     1638        if (fDoPlay)
     1639        {
     1640            if (pHstStream->tsLastPlayedCapturedMs < 2)
     1641                break;
     1642
     1643            uint32_t cfToPlay;
     1644            if (fJustStarted)
     1645                cfToPlay = pHstStream->Cfg.Backend.cfPeriod;
     1646            else
     1647                cfToPlay = DrvAudioHlpMsToFrames(&pHstStream->Cfg.Props, tsDeltaPlayedCapturedMs);
     1648
     1649            Log3Func(("[%s] Buffer: fJustStarted=%RTbool, cfLive=%RU32, cfToPlay=%RU32\n",
     1650                      pHstStream->szName, fJustStarted, cfLive, cfToPlay));
     1651
     1652            /* Did we reach a buffer underrun? Do pre-buffering again.
     1653             * If we're in pending disabled mode, try to play (drain) the remaining audio data. */
     1654            if (   !(pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE)
     1655                && !fJustStarted
     1656                && cfLive < cfToPlay)
     1657            {
     1658                pHstStream->fThresholdReached = false;
     1659                LogRel2(("Audio: Stream '%s' buffer underrun (total %RU8%%, which is %RU8%% of a period), buffering ...\n",
     1660                         pHstStream->szName, uLivePercent, (100 * cfLive) / pHstStream->Cfg.Backend.cfPeriod));
     1661                break;
     1662            }
     1663
     1664            if (cfToPlay > cfLive) /* Don't try to play more than available. */
     1665                cfToPlay = cfLive;
     1666
     1667            Log3Func(("[%s] Buffer: Playing %RU32 frames (%RU64ms since last playback)\n",
     1668                      pHstStream->szName, cfToPlay, tsDeltaPlayedCapturedMs));
     1669
     1670            if (pThis->pHostDrvAudio->pfnStreamPlayBegin)
     1671                pThis->pHostDrvAudio->pfnStreamPlayBegin(pThis->pHostDrvAudio, pHstStream->pvBackend);
     1672
     1673            if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
     1674            {
     1675                rc = drvAudioStreamPlayNonInterleaved(pThis, pHstStream, cfToPlay, &cfPlayedTotal);
     1676            }
     1677            else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
     1678            {
     1679                rc = drvAudioStreamPlayRaw(pThis, pHstStream, cfToPlay, &cfPlayedTotal);
     1680            }
     1681            else
     1682                AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
     1683
     1684            if (pThis->pHostDrvAudio->pfnStreamPlayEnd)
     1685                pThis->pHostDrvAudio->pfnStreamPlayEnd(pThis->pHostDrvAudio, pHstStream->pvBackend);
     1686        }
     1687
    15341688        if (RT_SUCCESS(rc))
    15351689        {
     
    15411695            STAM_COUNTER_ADD     (&pHstStream->Out.StatFramesPlayed, cfPlayedTotal);
    15421696#endif
     1697        }
     1698
     1699    } while (0);
    15431700
    15441701#ifdef LOG_ENABLED
    1545             cfLive = AudioMixBufLive(&pHstStream->MixBuf);
    1546 #endif
    1547         }
    1548 
    1549 #ifdef LOG_ENABLED
    1550         pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
    1551         Log3Func(("[%s] End: stsBackend=%s, cfLive=%RU32, cfPlayedTotal=%RU32, rc=%Rrc\n",
    1552                   pHstStream->szName, pszBackendSts, cfLive, cfPlayedTotal, rc));
    1553         RTStrFree(pszBackendSts);
     1702    uint32_t cfLive = AudioMixBufLive(&pHstStream->MixBuf);
     1703    pszStreamSts  = dbgAudioStreamStatusToStr(stsHstStream);
     1704    Log3Func(("[%s] End: stsHstStream=%s, cfLive=%RU32, cfPlayedTotal=%RU32, rc=%Rrc\n",
     1705              pHstStream->szName, pszStreamSts, cfLive, cfPlayedTotal, rc));
     1706    RTStrFree(pszStreamSts);
    15541707#endif /* LOG_ENABLED */
    1555 
    1556     } while (0);
    15571708
    15581709    int rc2 = RTCritSectLeave(&pThis->CritSect);
     
    17911942         */
    17921943
    1793         AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
    1794         PDMAUDIOSTREAMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend);
     1944        PDMAUDIOSTREAMSTS stsHstStream = pHstStream->fStatus;
    17951945#ifdef LOG_ENABLED
    1796         char *pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
    1797         Log3Func(("[%s] Start: stsBackend=%s\n", pHstStream->szName, pszBackendSts));
    1798         RTStrFree(pszBackendSts);
     1946        char *pszStreamSts = dbgAudioStreamStatusToStr(stsHstStream);
     1947        Log3Func(("[%s] Start: stsHstStream=%s\n", pHstStream->szName, pszStreamSts));
     1948        RTStrFree(pszStreamSts);
    17991949#endif /* LOG_ENABLED */
    1800         if (!(stsBackend & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Backend disabled? Bail out. */
     1950        if (!(stsHstStream & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Stream disabled? Bail out. */
    18011951            break;
    18021952
     
    18231973
    18241974#ifdef LOG_ENABLED
    1825         pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
    1826         Log3Func(("[%s] End: stsBackend=%s, cfCaptured=%RU32, rc=%Rrc\n",
    1827                   pHstStream->szName, pszBackendSts, cfCaptured, rc));
    1828         RTStrFree(pszBackendSts);
     1975        pszStreamSts = dbgAudioStreamStatusToStr(stsHstStream);
     1976        Log3Func(("[%s] End: stsHstStream=%s, cfCaptured=%RU32, rc=%Rrc\n",
     1977                  pHstStream->szName, pszStreamSts, cfCaptured, rc));
     1978        RTStrFree(pszStreamSts);
    18291979#endif /* LOG_ENABLED */
    18301980
     
    21912341
    21922342/**
     2343 * Retrieves an audio configuration from the specified CFGM node.
     2344 *
     2345 * @return VBox status code.
     2346 * @param  pThis                Driver instance to be called.
     2347 * @param  pCfg                 Where to store the retrieved audio configuration to.
     2348 * @param  pNode                Where to get the audio configuration from.
     2349 */
     2350static int drvAudioGetCfgFromCFGM(PDRVAUDIO pThis, PDRVAUDIOCFG pCfg, PCFGMNODE pNode)
     2351{
     2352    RT_NOREF(pThis);
     2353
     2354    /* Debug stuff. */
     2355    CFGMR3QueryBoolDef(pNode, "DebugEnabled",      &pCfg->Dbg.fEnabled,  false);
     2356    int rc2 = CFGMR3QueryString(pNode, "DebugPathOut", pCfg->Dbg.szPathOut, sizeof(pCfg->Dbg.szPathOut));
     2357    if (   RT_FAILURE(rc2)
     2358        || !strlen(pCfg->Dbg.szPathOut))
     2359    {
     2360        RTStrPrintf(pCfg->Dbg.szPathOut, sizeof(pCfg->Dbg.szPathOut), VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH);
     2361    }
     2362
     2363    if (pCfg->Dbg.fEnabled)
     2364        LogRel(("Audio: Debugging enabled (audio data written to '%s')\n", pCfg->Dbg.szPathOut));
     2365
     2366    /* Buffering stuff. */
     2367    CFGMR3QueryU32Def(pNode, "PeriodMs",     &pCfg->uPeriodMs, 0);
     2368    CFGMR3QueryU32Def(pNode, "BufferSizeMs", &pCfg->uBufferSizeMs, 0);
     2369    CFGMR3QueryU32Def(pNode, "PreBufferMs",  &pCfg->uPreBufMs, UINT32_MAX /* No custom value set */);
     2370
     2371    LogFunc(("pCfg=%p, uPeriodMs=%RU32, uBufferSizeMs=%RU32, uPreBufMs=%RU32\n",
     2372             pCfg, pCfg->uPeriodMs, pCfg->uBufferSizeMs, pCfg->uPreBufMs));
     2373
     2374    return VINF_SUCCESS;
     2375}
     2376
     2377/**
    21932378 * Intializes an audio driver instance.
    21942379 *
     
    22272412    CFGMR3QueryBoolDef(pThis->pCFGMNode, "OutputEnabled", &pThis->Out.fEnabled,  false);
    22282413
    2229     /* Input configuration. */
    2230     /** @todo Separate debugging stuff if needed. Later. */
    2231     CFGMR3QueryBoolDef(pThis->pCFGMNode, "DebugEnabled",      &pThis->In.Cfg.Dbg.fEnabled,  false);
    2232     rc2 = CFGMR3QueryString(pThis->pCFGMNode, "DebugPathOut", pThis->In.Cfg.Dbg.szPathOut, sizeof(pThis->In.Cfg.Dbg.szPathOut));
    2233     if (   RT_FAILURE(rc2)
    2234         || !strlen(pThis->In.Cfg.Dbg.szPathOut))
    2235     {
    2236         RTStrPrintf(pThis->In.Cfg.Dbg.szPathOut, sizeof(pThis->In.Cfg.Dbg.szPathOut), VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH);
    2237     }
    2238 
    2239     if (pThis->In.Cfg.Dbg.fEnabled)
    2240         LogRel(("Audio: Debugging input enabled (audio data written to '%s')\n", pThis->In.Cfg.Dbg.szPathOut));
    2241 
    2242     /* Output configuration. */
    2243     CFGMR3QueryBoolDef(pThis->pCFGMNode, "DebugEnabled",      &pThis->Out.Cfg.Dbg.fEnabled, false);
    2244     rc2 = CFGMR3QueryString(pThis->pCFGMNode, "DebugPathOut", pThis->Out.Cfg.Dbg.szPathOut, sizeof(pThis->Out.Cfg.Dbg.szPathOut));
    2245     if (   RT_FAILURE(rc2)
    2246         || !strlen(pThis->Out.Cfg.Dbg.szPathOut))
    2247     {
    2248         RTStrPrintf(pThis->Out.Cfg.Dbg.szPathOut, sizeof(pThis->Out.Cfg.Dbg.szPathOut), VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH);
    2249     }
    2250 
    2251     if (pThis->Out.Cfg.Dbg.fEnabled)
    2252         LogRel(("Audio: Debugging output enabled (audio data written to '%s')\n", pThis->Out.Cfg.Dbg.szPathOut));
    2253 
    22542414    LogRel2(("Audio: Initial status for driver '%s': Input is %s, output is %s\n",
    22552415             pThis->szName, pThis->In.fEnabled ? "enabled" : "disabled", pThis->Out.fEnabled ? "enabled" : "disabled"));
     2416
     2417    /*
     2418     * Load configurations.
     2419     */
     2420    rc = drvAudioGetCfgFromCFGM(pThis, &pThis->In.Cfg, pThis->pCFGMNode);
     2421    if (RT_SUCCESS(rc))
     2422        rc = drvAudioGetCfgFromCFGM(pThis, &pThis->Out.Cfg, pThis->pCFGMNode);
    22562423
    22572424    LogFunc(("[%s] rc=%Rrc\n", pThis->szName, rc));
     
    30633230 * @param   pHstStream          (Host) audio stream to use for creating the stream on the backend side.
    30643231 * @param   pCfgReq             Requested audio stream configuration to use for stream creation.
    3065  * @param   pCfgAcq             Acquired audio stream configuration returned by the backend. Optional, can be NULL.
     3232 * @param   pCfgAcq             Acquired audio stream configuration returned by the backend.
    30663233 */
    30673234static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis,
     
    30713238    AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
    30723239    AssertPtrReturn(pCfgReq,    VERR_INVALID_POINTER);
    3073     /* pCfgAcq is optional. */
     3240    AssertPtrReturn(pCfgAcq,    VERR_INVALID_POINTER);
    30743241
    30753242    AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
     
    30793246              ("Stream '%s' already initialized in backend\n", pHstStream->szName));
    30803247
     3248    /* Get the right configuration for the stream to be created. */
     3249    PDRVAUDIOCFG pDrvCfg = pCfgReq->enmDir == PDMAUDIODIR_IN ? &pThis->In.Cfg : &pThis->Out.Cfg;
     3250
     3251    /* Fill in the tweakable parameters into the requested host configuration.
     3252     * All parameters in principle can be changed and returned by the backend via the acquired configuration. */
     3253
     3254    /*
     3255     * Period size
     3256     */
     3257    if (pDrvCfg->uPeriodMs)
     3258        pCfgReq->Backend.cfPeriod = DrvAudioHlpMsToFrames(&pCfgReq->Props, pDrvCfg->uPeriodMs);
     3259    else /* Set default period size. */
     3260        pCfgReq->Backend.cfPeriod = DrvAudioHlpMsToFrames(&pCfgReq->Props, 50 /* ms */);
     3261
     3262    LogRel2(("Audio: Using %s period size (%RU32ms, %RU32 frames) for stream '%s'\n",
     3263             pDrvCfg->uPeriodMs ? "custom" : "default", DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfPeriod),
     3264             pCfgReq->Backend.cfPeriod, pHstStream->szName));
     3265
     3266    /*
     3267     * Buffer size
     3268     */
     3269    if (pDrvCfg->uBufferSizeMs)
     3270        pCfgReq->Backend.cfBufferSize = DrvAudioHlpMsToFrames(&pCfgReq->Props, pDrvCfg->uBufferSizeMs);
     3271    else /* Set default buffer size. */
     3272        pCfgReq->Backend.cfBufferSize = DrvAudioHlpMsToFrames(&pCfgReq->Props, 200 /* ms */);
     3273
     3274    LogRel2(("Audio: Using %s buffer size (%RU32ms, %RU32 frames) for stream '%s'\n",
     3275             pDrvCfg->uBufferSizeMs ? "custom" : "default", DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize),
     3276             pCfgReq->Backend.cfBufferSize, pHstStream->szName));
     3277
     3278    /*
     3279     * Pre-buffering size
     3280     */
     3281    if (pDrvCfg->uPreBufMs != UINT32_MAX)
     3282    {
     3283        if (!pDrvCfg->uPreBufMs) /* Pre-buffering is set to disabled. */
     3284            LogRel2(("Audio: Using custom pre-buffering (disabled) for stream '%s'\n", pHstStream->szName));
     3285        pCfgReq->Backend.cfPreBuf = DrvAudioHlpMsToFrames(&pCfgReq->Props, pDrvCfg->uPreBufMs);
     3286    }
     3287    else /* Set default pre-buffering size. */
     3288        pCfgReq->Backend.cfPreBuf = pCfgReq->Backend.cfBufferSize;
     3289
     3290    LogRel2(("Audio: Using %s pre-buffering size (%RU32ms, %RU32 frames) for stream '%s'\n",
     3291             pDrvCfg->uPreBufMs != UINT32_MAX ? "custom" : "default", DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfPreBuf),
     3292             pCfgReq->Backend.cfPreBuf, pHstStream->szName));
     3293
     3294    /*
     3295     * Validate input.
     3296     */
     3297    if (pCfgReq->Backend.cfBufferSize < pCfgReq->Backend.cfPeriod)
     3298    {
     3299        LogRel(("Audio: Error for stream '%s': Buffer size (%RU32ms) must not be smaller than the period size (%RU32ms)\n",
     3300                pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize),
     3301                DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfPeriod)));
     3302        return VERR_INVALID_PARAMETER;
     3303    }
     3304
     3305    if (   pCfgReq->Backend.cfPreBuf != UINT32_MAX /* Custom pre-buffering set? */
     3306        && pCfgReq->Backend.cfPreBuf)
     3307    {
     3308        if (pCfgReq->Backend.cfBufferSize < pCfgReq->Backend.cfPreBuf)
     3309        {
     3310            LogRel(("Audio: Error for stream '%s': Pre-buffering size (%RU32ms) must not be bigger than the buffer size (%RU32ms)\n",
     3311                    pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfPreBuf),
     3312                    DrvAudioHlpFramesToMs(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize)));
     3313            return VERR_INVALID_PARAMETER;
     3314        }
     3315    }
     3316
    30813317    /* Make the acquired host configuration the requested host configuration initially,
    30823318     * in case the backend does not report back an acquired configuration. */
    3083     PDMAUDIOSTREAMCFG CfgAcq;
    3084     int rc = DrvAudioHlpStreamCfgCopy(&CfgAcq, pCfgReq);
     3319    int rc = DrvAudioHlpStreamCfgCopy(pCfgAcq, pCfgReq);
    30853320    if (RT_FAILURE(rc))
    30863321    {
     
    30903325    }
    30913326
    3092     rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream->pvBackend, pCfgReq, &CfgAcq);
     3327    rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream->pvBackend, pCfgReq, pCfgAcq);
    30933328    if (RT_FAILURE(rc))
    30943329    {
     
    31023337
    31033338    /* Validate acquired configuration. */
    3104     if (!DrvAudioHlpStreamCfgIsValid(&CfgAcq))
     3339    if (!DrvAudioHlpStreamCfgIsValid(pCfgAcq))
    31053340    {
    31063341        LogRel(("Audio: Creating stream '%s' returned an invalid backend configuration, skipping\n", pHstStream->szName));
    31073342        return VERR_INVALID_PARAMETER;
     3343    }
     3344
     3345    /* Let the user know that the backend changed one of the user's custom values requested above. */
     3346    if (   pDrvCfg->uBufferSizeMs
     3347        && pCfgAcq->Backend.cfBufferSize != pCfgReq->Backend.cfBufferSize)
     3348    {
     3349        LogRel2(("Audio: Custom buffer size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
     3350                 pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgAcq->Props, pCfgAcq->Backend.cfBufferSize), pCfgAcq->Backend.cfBufferSize));
     3351    }
     3352
     3353    if (   pDrvCfg->uPeriodMs
     3354        && pCfgAcq->Backend.cfPeriod != pCfgReq->Backend.cfPeriod)
     3355    {
     3356        LogRel2(("Audio: Custom period size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
     3357                 pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgAcq->Props, pCfgAcq->Backend.cfPeriod), pCfgAcq->Backend.cfPeriod));
     3358    }
     3359
     3360    if (   pDrvCfg->uPreBufMs != UINT32_MAX
     3361        && pCfgAcq->Backend.cfPreBuf != pCfgReq->Backend.cfPreBuf)
     3362    {
     3363        LogRel2(("Audio: Custom pre-buffering size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
     3364                 pHstStream->szName, DrvAudioHlpFramesToMs(&pCfgAcq->Props, pCfgAcq->Backend.cfPreBuf), pCfgAcq->Backend.cfPreBuf));
    31083365    }
    31093366
     
    31123369     * at some later point in time. */
    31133370    pHstStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
    3114 
    3115     if (pCfgAcq)
    3116     {
    3117         int rc2 = DrvAudioHlpStreamCfgCopy(pCfgAcq, &CfgAcq);
    3118         AssertRC(rc2);
    3119     }
    31203371
    31213372    return VINF_SUCCESS;
  • trunk/src/VBox/Devices/Audio/DrvAudio.h

    r73230 r73370  
    7979typedef struct DRVAUDIOCFG
    8080{
    81     /** Configures the period size (in audio frames).
    82      *  This value reflects the number of audio frames in between each hardware interrupt on the
     81    /** Configures the period size (in ms).
     82     *  This value reflects the time in between each hardware interrupt on the
    8383     *  backend (host) side. */
    84     uint32_t             cfPeriod;
    85     /** Configures the (ring) buffer size (in audio frames). Often is a multiple of cfPeriod. */
    86     uint32_t             cfBufferSize;
    87     /** Configures the pre-buffering size (in audio frames).
    88      *  Frames needed in buffer before the stream becomes active (pre buffering).
    89      *  The bigger this value is, the more latency for the stream will occur. */
    90     uint32_t             cfPreBuf;
     84    uint32_t             uPeriodMs;
     85    /** Configures the (ring) buffer size (in ms). Often is a multiple of uPeriodMs. */
     86    uint32_t             uBufferSizeMs;
     87    /** Configures the pre-buffering size (in ms).
     88     *  Time needed in buffer before the stream becomes active (pre buffering).
     89     *  The bigger this value is, the more latency for the stream will occur.
     90     *  Set to 0 to disable pre-buffering completely.
     91     *  By default set to UINT32_MAX if not set to a custom value. */
     92    uint32_t             uPreBufMs;
    9193    /** The driver's debugging configuration. */
    9294    struct
  • trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp

    r73097 r73370  
    123123static ALSAAUDIOCFG s_ALSAConf =
    124124{
    125 #ifdef HIGH_LATENCY
    126     1,
    127     1,
    128 #else
    129125    0,
    130126    0,
    131 #endif
    132127    "default",
    133128    "default",
    134 #ifdef HIGH_LATENCY
    135     400000,
    136     400000 / 4,
    137     400000,
    138     400000 / 4,
    139 #else
    140 # define DEFAULT_BUFFER_SIZE 1024
    141 # define DEFAULT_PERIOD_SIZE 256
    142     DEFAULT_BUFFER_SIZE * 4,
    143     DEFAULT_PERIOD_SIZE * 4,
    144     DEFAULT_BUFFER_SIZE,
    145     DEFAULT_PERIOD_SIZE,
    146 #endif
    147     0,
     129# define DEFAULT_PERIOD_FRAMES 4410                      /* 100ms for 44,1 kHz. */
     130# define DEFAULT_BUFFER_FRAMES DEFAULT_PERIOD_FRAMES * 2 /* Double (buffering) the period size. */
     131    DEFAULT_BUFFER_FRAMES,
     132    DEFAULT_PERIOD_FRAMES,
     133    DEFAULT_BUFFER_FRAMES,
     134    DEFAULT_PERIOD_FRAMES,
     135    DEFAULT_PERIOD_FRAMES * 2,                           /* Threshold, using double the period size to avoid stutters. */
    148136    0,
    149137    0,
     
    180168    int                 resample;
    181169    int                 nchannels;
     170    /** Buffer size (in audio frames). */
    182171    unsigned long       buffer_size;
     172    /** Periods (in audio frames). */
    183173    unsigned long       period_size;
    184     snd_pcm_uframes_t   samples;
     174    /** For playback: Starting to play threshold (in audio frames).
     175     *  For Capturing: Starting to capture threshold (in audio frames). */
     176    unsigned long       threshold;
    185177} ALSAAUDIOSTREAMCFG, *PALSAAUDIOSTREAMCFG;
    186178
     
    288280
    289281
    290 static int alsaGetSampleShift(snd_pcm_format_t fmt, unsigned *puShift)
    291 {
    292     AssertPtrReturn(puShift, VERR_INVALID_POINTER);
    293 
    294     switch (fmt)
    295     {
    296         case SND_PCM_FORMAT_S8:
    297         case SND_PCM_FORMAT_U8:
    298             *puShift = 0;
    299             break;
    300 
    301         case SND_PCM_FORMAT_S16_LE:
    302         case SND_PCM_FORMAT_U16_LE:
    303         case SND_PCM_FORMAT_S16_BE:
    304         case SND_PCM_FORMAT_U16_BE:
    305             *puShift = 1;
    306             break;
    307 
    308         case SND_PCM_FORMAT_S32_LE:
    309         case SND_PCM_FORMAT_U32_LE:
    310         case SND_PCM_FORMAT_S32_BE:
    311         case SND_PCM_FORMAT_U32_BE:
    312             *puShift = 2;
    313             break;
    314 
    315         default:
    316             AssertMsgFailed(("Format %ld not supported\n", fmt));
    317             return VERR_NOT_SUPPORTED;
    318     }
    319 
    320     return VINF_SUCCESS;
    321 }
    322 
    323 
    324282static int alsaStreamSetThreshold(snd_pcm_t *phPCM, snd_pcm_uframes_t threshold)
    325283{
     
    395353    }
    396354
     355    LogFlowFuncLeaveRC(rc);
    397356    return rc;
    398357}
     
    612571        }
    613572
     573        LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev));
     574
    614575        int err = snd_pcm_open(&phPCM, pszDev,
    615576                               fIn ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
     
    622583        }
    623584
    624         LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev));
     585        err = snd_pcm_nonblock(phPCM, 1);
     586        if (err < 0)
     587        {
     588            LogRel(("ALSA: Error setting output non-blocking mode: %s\n", snd_strerror(err)));
     589            rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     590            break;
     591        }
    625592
    626593        snd_pcm_hw_params_t *pHWParms;
     
    683650            if (!buffer_size)
    684651            {
    685                 buffer_size = DEFAULT_BUFFER_SIZE;
    686                 period_size = DEFAULT_PERIOD_SIZE;
     652                buffer_size = DEFAULT_BUFFER_FRAMES;
     653                period_size = DEFAULT_PERIOD_FRAMES;
    687654            }
    688655        }
     
    792759                err = snd_pcm_hw_params_set_buffer_size_near(phPCM,
    793760                                                             pHWParms, &buffer_size_f);
    794                 LogFunc(("Buffer size is: %RU32\n", buffer_size_f));
    795761                if (err < 0)
    796762                {
    797                     LogRel(("ALSA: Failed to set buffer size %d: %s\n",
    798                             buffer_size_f, snd_strerror(err)));
     763                    LogRel(("ALSA: Failed to set near buffer size %RU32: %s\n", buffer_size_f, snd_strerror(err)));
    799764                    rc = VERR_AUDIO_BACKEND_INIT_FAILED;
    800765                    break;
     
    821786        }
    822787
    823         LogFunc(("Buffer sample size is: %RU32\n", obt_buffer_size));
    824 
    825788        snd_pcm_uframes_t obt_period_size;
    826789        int dir = 0;
     
    833796        }
    834797
    835         LogFunc(("Freq=%dHz, period size=%RU32, buffer size=%RU32\n",
     798        LogRel2(("ALSA: Frequency is %dHz, period size is %RU32 frames, buffer size is %RU32 fames\n",
    836799                 pCfgReq->freq, obt_period_size, obt_buffer_size));
    837800
     
    845808
    846809        if (   !fIn
    847             && s_ALSAConf.threshold)
    848         {
    849             unsigned uShift;
    850             rc = alsaGetSampleShift(pCfgReq->fmt, &uShift);
    851             if (RT_SUCCESS(rc))
    852             {
    853                 int bytes_per_sec = uFreq
    854                     << (cChannels == 2)
    855                     << uShift;
    856 
    857                 snd_pcm_uframes_t threshold
    858                     = (s_ALSAConf.threshold * bytes_per_sec) / 1000;
    859 
    860                 rc = alsaStreamSetThreshold(phPCM, threshold);
    861             }
    862         }
    863         else
    864             rc = VINF_SUCCESS;
     810            && pCfgReq->threshold)
     811        {
     812            rc = alsaStreamSetThreshold(phPCM, pCfgReq->threshold);
     813            if (RT_FAILURE(rc))
     814                break;
     815
     816            LogRel2(("ALSA: Threshold for playback set to %RU32 frames\n", pCfgReq->threshold));
     817        }
     818
     819        pCfgObt->fmt         = pCfgReq->fmt;
     820        pCfgObt->nchannels   = cChannels;
     821        pCfgObt->freq        = uFreq;
     822        pCfgObt->period_size = obt_period_size;
     823        pCfgObt->buffer_size = obt_buffer_size;
     824        pCfgObt->threshold   = pCfgReq->threshold;
     825
     826        rc = VINF_SUCCESS;
    865827    }
    866828    while (0);
     
    868830    if (RT_SUCCESS(rc))
    869831    {
    870         pCfgObt->fmt       = pCfgReq->fmt;
    871         pCfgObt->nchannels = cChannels;
    872         pCfgObt->freq      = uFreq;
    873         pCfgObt->samples   = obt_buffer_size;
    874 
    875832        *pphPCM = phPCM;
    876833    }
     
    953910
    954911    return VINF_SUCCESS;
    955 }
    956 
    957 
    958 static int drvHostALSAAudioStreamCtl(PALSAAUDIOSTREAM pStreamALSA, bool fPause)
    959 {
    960     int rc = VINF_SUCCESS;
    961 
    962     const bool fInput = pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN;
    963 
    964     int err;
    965     if (fPause)
    966     {
    967         err = snd_pcm_drop(pStreamALSA->phPCM);
    968         if (err < 0)
    969         {
    970             LogRel(("ALSA: Error stopping %s stream: %s\n", fInput ? "input" : "output", snd_strerror(err)));
    971             rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    972         }
    973     }
    974     else
    975     {
    976         err = snd_pcm_prepare(pStreamALSA->phPCM);
    977         if (err < 0)
    978         {
    979             LogRel(("ALSA: Error preparing %s stream: %s\n", fInput ? "input" : "output", snd_strerror(err)));
    980             rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    981         }
    982         else
    983         {
    984             Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
    985 
    986             if (fInput) /* Only start the PCM stream for input streams. */
    987             {
    988                 err = snd_pcm_start(pStreamALSA->phPCM);
    989                 if (err < 0)
    990                 {
    991                     LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err)));
    992                     rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    993                 }
    994             }
    995         }
    996     }
    997 
    998     return rc;
    999912}
    1000913
     
    13241237        req.freq        = pCfgReq->Props.uHz;
    13251238        req.nchannels   = pCfgReq->Props.cChannels;
    1326         req.period_size = s_ALSAConf.period_size_out; /** @todo Make this configurable. */
    1327         req.buffer_size = s_ALSAConf.buffer_size_out; /** @todo Make this configurable. */
     1239        req.period_size = pCfgReq->Backend.cfPeriod;
     1240        req.buffer_size = pCfgReq->Backend.cfBufferSize;
     1241        req.threshold   = pCfgReq->Backend.cfPreBuf;
    13281242
    13291243        ALSAAUDIOSTREAMCFG obt;
     
    13391253            break;
    13401254
    1341         pCfgAcq->cFrameBufferHint = obt.samples * 4;
    1342 
    1343         AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
    1344 
    1345         size_t cbBuf = obt.samples * PDMAUDIOSTREAMCFG_F2B(pCfgAcq, 1);
    1346         AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
    1347 
    1348         pStreamALSA->pvBuf = RTMemAllocZ(cbBuf);
     1255        pCfgAcq->Backend.cfPeriod     = obt.period_size;
     1256        pCfgAcq->Backend.cfBufferSize = obt.buffer_size;
     1257        pCfgAcq->Backend.cfPreBuf     = obt.threshold;
     1258
     1259        pStreamALSA->cbBuf = pCfgAcq->Backend.cfBufferSize * DrvAudioHlpPCMPropsBytesPerFrame(&pCfgAcq->Props);
     1260        pStreamALSA->pvBuf = RTMemAllocZ(pStreamALSA->cbBuf);
    13491261        if (!pStreamALSA->pvBuf)
    13501262        {
    1351             LogRel(("ALSA: Not enough memory for output DAC buffer (%RU32 samples, %zu bytes)\n", obt.samples, cbBuf));
     1263            LogRel(("ALSA: Not enough memory for output DAC buffer (%zu frames)\n", pCfgAcq->Backend.cfBufferSize));
    13521264            rc = VERR_NO_MEMORY;
    13531265            break;
    13541266        }
    13551267
    1356         pStreamALSA->cbBuf = cbBuf;
    13571268        pStreamALSA->phPCM = phPCM;
    13581269    }
     
    13791290        req.freq        = pCfgReq->Props.uHz;
    13801291        req.nchannels   = pCfgReq->Props.cChannels;
    1381         req.period_size = s_ALSAConf.period_size_in; /** @todo Make this configurable. */
    1382         req.buffer_size = s_ALSAConf.buffer_size_in; /** @todo Make this configurable. */
     1292        req.period_size = DrvAudioHlpMsToFrames(&pCfgReq->Props, 50 /* ms */); /** @todo Make this configurable. */
     1293        req.buffer_size = req.period_size * 2; /** @todo Make this configurable. */
     1294        req.threshold   = req.period_size;
    13831295
    13841296        ALSAAUDIOSTREAMCFG obt;
     
    13941306            break;
    13951307
    1396         pCfgAcq->cFrameBufferHint = obt.samples;
    1397 
    1398         AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
    1399 
    1400         size_t cbBuf = obt.samples * PDMAUDIOSTREAMCFG_F2B(pCfgAcq, 1);
    1401         AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
    1402 
    1403         pStreamALSA->pvBuf = RTMemAlloc(cbBuf);
     1308        pCfgAcq->Backend.cfPeriod     = obt.period_size;
     1309        pCfgAcq->Backend.cfBufferSize = obt.buffer_size;
     1310        /* No pre-buffering. */
     1311
     1312        pStreamALSA->cbBuf = pCfgAcq->Backend.cfBufferSize * DrvAudioHlpPCMPropsBytesPerFrame(&pCfgAcq->Props);
     1313        pStreamALSA->pvBuf = RTMemAlloc(pStreamALSA->cbBuf);
    14041314        if (!pStreamALSA->pvBuf)
    14051315        {
    1406             LogRel(("ALSA: Not enough memory for input ADC buffer (%RU32 samples, %zu bytes)\n", obt.samples, cbBuf));
     1316            LogRel(("ALSA: Not enough memory for input ADC buffer (%zu frames)\n", pCfgAcq->Backend.cfBufferSize));
    14071317            rc = VERR_NO_MEMORY;
    14081318            break;
    14091319        }
    14101320
    1411         pStreamALSA->cbBuf = cbBuf;
    14121321        pStreamALSA->phPCM = phPCM;
    14131322    }
     
    14241333static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
    14251334{
    1426     int rc;
     1335    int rc = VINF_SUCCESS;
     1336
     1337    int err;
     1338
    14271339    switch (enmStreamCmd)
    14281340    {
    14291341        case PDMAUDIOSTREAMCMD_ENABLE:
    14301342        case PDMAUDIOSTREAMCMD_RESUME:
    1431             rc = drvHostALSAAudioStreamCtl(pStreamALSA, false /* fStop */);
    1432             break;
     1343        {
     1344            err = snd_pcm_prepare(pStreamALSA->phPCM);
     1345            if (err < 0)
     1346            {
     1347                LogRel(("ALSA: Error preparing input stream: %s\n", snd_strerror(err)));
     1348                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1349            }
     1350            else
     1351            {
     1352                Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
     1353
     1354                /* Only start the PCM stream for input streams. */
     1355                err = snd_pcm_start(pStreamALSA->phPCM);
     1356                if (err < 0)
     1357                {
     1358                    LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err)));
     1359                    rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1360                }
     1361            }
     1362
     1363            break;
     1364        }
    14331365
    14341366        case PDMAUDIOSTREAMCMD_DISABLE:
     1367        {
     1368            err = snd_pcm_drop(pStreamALSA->phPCM);
     1369            if (err < 0)
     1370            {
     1371                LogRel(("ALSA: Error disabling input %s stream: %s\n", snd_strerror(err)));
     1372                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1373            }
     1374            break;
     1375        }
     1376
    14351377        case PDMAUDIOSTREAMCMD_PAUSE:
    1436             rc = drvHostALSAAudioStreamCtl(pStreamALSA, true /* fStop */);
    1437             break;
     1378        {
     1379            err = snd_pcm_drop(pStreamALSA->phPCM);
     1380            if (err < 0)
     1381            {
     1382                LogRel(("ALSA: Error pausing input stream: %s\n", snd_strerror(err)));
     1383                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1384            }
     1385            break;
     1386        }
    14381387
    14391388        default:
    1440             AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
    1441             rc = VERR_INVALID_PARAMETER;
    1442             break;
    1443     }
    1444 
     1389            rc = VERR_NOT_SUPPORTED;
     1390            break;
     1391    }
     1392
     1393    LogFlowFuncLeaveRC(rc);
    14451394    return rc;
    14461395}
     
    14491398static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
    14501399{
    1451     int rc;
     1400    int rc = VINF_SUCCESS;
     1401
     1402    int err;
     1403
    14521404    switch (enmStreamCmd)
    14531405    {
    14541406        case PDMAUDIOSTREAMCMD_ENABLE:
    14551407        case PDMAUDIOSTREAMCMD_RESUME:
    1456             rc = drvHostALSAAudioStreamCtl(pStreamALSA, false /* fStop */);
    1457             break;
     1408        {
     1409            err = snd_pcm_prepare(pStreamALSA->phPCM);
     1410            if (err < 0)
     1411            {
     1412                LogRel(("ALSA: Error preparing output stream: %s\n", snd_strerror(err)));
     1413                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1414            }
     1415            else
     1416            {
     1417                Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
     1418            }
     1419
     1420            break;
     1421        }
    14581422
    14591423        case PDMAUDIOSTREAMCMD_DISABLE:
     1424        {
     1425            err = snd_pcm_drop(pStreamALSA->phPCM);
     1426            if (err < 0)
     1427            {
     1428                LogRel(("ALSA: Error disabling output stream: %s\n", snd_strerror(err)));
     1429                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1430            }
     1431            break;
     1432        }
     1433
    14601434        case PDMAUDIOSTREAMCMD_PAUSE:
    1461             rc = drvHostALSAAudioStreamCtl(pStreamALSA, true /* fStop */);
    1462             break;
     1435        {
     1436            err = snd_pcm_drop(pStreamALSA->phPCM);
     1437            if (err < 0)
     1438            {
     1439                LogRel(("ALSA: Error pausing output stream: %s\n", snd_strerror(err)));
     1440                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1441            }
     1442            break;
     1443        }
     1444
     1445        case PDMAUDIOSTREAMCMD_DRAIN:
     1446        {
     1447            snd_pcm_state_t streamState = snd_pcm_state(pStreamALSA->phPCM);
     1448            Log2Func(("Stream state is: %d\n", streamState));
     1449
     1450            if (   streamState == SND_PCM_STATE_PREPARED
     1451                || streamState == SND_PCM_STATE_RUNNING)
     1452            {
     1453                err = snd_pcm_nonblock(pStreamALSA->phPCM, 0);
     1454                if (err < 0)
     1455                {
     1456                    LogRel(("ALSA: Error disabling output non-blocking mode: %s\n", snd_strerror(err)));
     1457                    rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1458                    break;
     1459                }
     1460
     1461                err = snd_pcm_drain(pStreamALSA->phPCM);
     1462                if (err < 0)
     1463                {
     1464                    LogRel(("ALSA: Error draining output: %s\n", snd_strerror(err)));
     1465                    rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1466                    break;
     1467                }
     1468
     1469                err = snd_pcm_nonblock(pStreamALSA->phPCM, 1);
     1470                if (err < 0)
     1471                {
     1472                    LogRel(("ALSA: Error re-enabling output non-blocking mode: %s\n", snd_strerror(err)));
     1473                    rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1474                }
     1475            }
     1476            break;
     1477        }
    14631478
    14641479        default:
    1465             AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
    1466             rc = VERR_INVALID_PARAMETER;
    1467             break;
    1468     }
    1469 
     1480            rc = VERR_NOT_SUPPORTED;
     1481            break;
     1482    }
     1483
     1484    LogFlowFuncLeaveRC(rc);
    14701485    return rc;
    14711486}
     
    16931708
    16941709/**
     1710 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
     1711 */
     1712static DECLCALLBACK(uint32_t) drvHostALSAStreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1713{
     1714    RT_NOREF(pInterface);
     1715    AssertPtrReturn(pStream, 0);
     1716
     1717    PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
     1718
     1719    snd_pcm_sframes_t cfDelay  = 0;
     1720    snd_pcm_state_t   enmState = snd_pcm_state(pStreamALSA->phPCM);
     1721
     1722    int rc = VINF_SUCCESS;
     1723
     1724    AssertPtr(pStreamALSA->pCfg);
     1725    if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_OUT)
     1726    {
     1727        int rc2 = snd_pcm_delay(pStreamALSA->phPCM, &cfDelay);
     1728        if (RT_SUCCESS(rc))
     1729            rc = rc2;
     1730
     1731        /* Make sure to check the stream's status.
     1732         * If it's anything but SND_PCM_STATE_RUNNING, the delay is meaningless and therefore 0. */
     1733        if (enmState != SND_PCM_STATE_RUNNING)
     1734            cfDelay = 0;
     1735    }
     1736
     1737    /* Note: For input streams we never have pending data left. */
     1738
     1739    Log2Func(("cfDelay=%RI32, enmState=%d, rc=%d\n", cfDelay, enmState, rc));
     1740
     1741    return DrvAudioHlpFramesToBytes(&pStreamALSA->pCfg->Props, cfDelay);
     1742}
     1743
     1744
     1745/**
    16951746 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
    16961747 */
     
    17521803    /* IHostAudio */
    17531804    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostALSAAudio);
     1805    pThis->IHostAudio.pfnStreamGetPending = drvHostALSAStreamGetPending;
    17541806
    17551807    return VINF_SUCCESS;
  • trunk/src/VBox/Devices/Audio/DrvHostCoreAudio.cpp

    r70318 r73370  
    55
    66/*
    7  * Copyright (C) 2010-2017 Oracle Corporation
     7 * Copyright (C) 2010-2018 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    4040
    4141
    42 /* Audio Queue buffer configuration. */
    43 #define AQ_BUF_COUNT    32      /* Number of buffers. */
    44 #define AQ_BUF_SIZE     512     /* Size of each buffer in bytes. */
    45 #define AQ_BUF_TOTAL    (AQ_BUF_COUNT * AQ_BUF_SIZE)
    46 #define AQ_BUF_SAMPLES  (AQ_BUF_TOTAL / 4)  /* Hardcoded 4 bytes per sample! */
    4742
    4843/* Enables utilizing the Core Audio converter unit for converting
     
    379374    AudioQueueRef               audioQueue;
    380375    /** The audio buffers which are used with the above audio queue. */
    381     AudioQueueBufferRef         audioBuffer[AQ_BUF_COUNT];
     376    AudioQueueBufferRef         audioBuffer[2];
    382377    /** The acquired (final) audio format for this stream. */
    383378    AudioStreamBasicDescription asbdStream;
     
    13031298        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
    13041299
    1305     const size_t cbBufSize = AQ_BUF_SIZE; /** @todo Make this configurable! */
     1300    const size_t cbBufSize = DrvAudioHlpFramesToBytes(&pCAStream->pCfg->Props, pCAStream->pCfg->Backend.cfPeriod);
    13061301
    13071302    /*
     
    23262321
    23272322            rc = coreAudioStreamInitQueue(pCAStream, pCfgReq, pCfgAcq);
    2328             if (RT_SUCCESS(rc))
    2329             {
    2330                 pCfgAcq->cFrameBufferHint = AQ_BUF_SAMPLES; /** @todo Make this configurable. */
    2331             }
    23322323            if (RT_SUCCESS(rc))
    23332324            {
  • trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp

    r73097 r73370  
    104104typedef struct DSOUNDHOSTCFG
    105105{
    106     unsigned int    msLatencyIn;
    107     unsigned int    msLatencyOut;
    108106    RTUUID          uuidPlay;
    109107    LPCGUID         pGuidPlay;
     
    115113{
    116114    /** The stream's acquired configuration. */
    117     PPDMAUDIOSTREAMCFG pCfg;
     115    PDMAUDIOSTREAMCFG  Cfg;
    118116    /** Buffer alignment. */
    119117    uint8_t            uAlign;
     
    150148            /** Offset of last play cursor within the DSB when last played. */
    151149            DWORD                       offPlayCursorLastPlayed;
    152             /** Total amount (in bytes) written. */
     150            /** Total amount (in bytes) written to our internal ring buffer. */
    153151            uint64_t                    cbWritten;
    154152            /** Total amount (in bytes) played (to the DirectSound buffer). */
    155             uint64_t                    cbPlayed;
    156             /** Flag indicating whether playback was (re)started. */
    157             bool                        fFirstPlayback;
    158             /** Timestamp (in ms) of When the last playback has happened. */
    159             uint64_t                    tsLastPlayMs;
     153            uint64_t                    cbTransferred;
     154            /** Flag indicating whether playback was just (re)started. */
     155            bool                        fFirstTransfer;
     156            /** How much (in bytes) the last transfer from the internal buffer
     157             *  to the DirectSound buffer was. */
     158            uint32_t                    cbLastTransferred;
     159            /** Timestamp (in ms) of the last transfer from the internal buffer
     160             *  to the DirectSound buffer. */
     161            uint64_t                    tsLastTransferred;
    160162            /** Number of buffer underruns happened. Used for logging. */
    161163            uint8_t                     cUnderruns;
    162164        } Out;
    163165    };
     166    struct
     167    {
     168        uint64_t tsLastTransferredMs;
     169    } Dbg;
    164170} DSOUNDSTREAM, *PDSOUNDSTREAM;
    165171
     
    194200    PFNPDMHOSTAUDIOCALLBACK     pfnCallback;
    195201#endif
    196     /** Stopped indicator. */
    197     bool                        fStopped;
    198     /** Shutdown indicator. */
    199     bool                        fShutdown;
    200     /** Notification thread. */
    201     RTTHREAD                    Thread;
    202     /** Array of events to wait for in notification thread. */
    203     HANDLE                      aEvents[VBOX_DSOUND_MAX_EVENTS];
    204202    /** Pointer to the input stream. */
    205203    PDSOUNDSTREAM               pDSStrmIn;
     
    240238*********************************************************************************************************************************/
    241239static HRESULT  directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB);
     240static HRESULT  directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS);
    242241static HRESULT  directSoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush);
    243242static HRESULT  directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush);
     
    245244static void     dsoundDeviceRemove(PDSOUNDDEV pDev);
    246245static int      dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg);
    247 
    248 static int      dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown);
    249246
    250247static void     dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis);
     
    292289    AssertPtrReturn(pdwFree,   VERR_INVALID_POINTER);
    293290
    294     Assert(pStreamDS->pCfg->enmDir == PDMAUDIODIR_OUT); /* Paranoia. */
     291    Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT); /* Paranoia. */
    295292
    296293    LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB;
     
    601598
    602599        RTCritSectLeave(&pThis->CritSect);
    603 
    604         int rc2 = dsoundNotifyThread(pThis, false /* fShutdown */);
    605         AssertRC(rc2);
    606600    }
    607601
     
    679673         * of copying own buffer data to our secondary's Direct Sound buffer.
    680674         */
    681         bd.dwFlags     = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE;
    682         bd.dwFlags    |= DSBCAPS_CTRLPOSITIONNOTIFY;
    683 
    684         bd.dwBufferBytes = DrvAudioHlpMsToBytes(&pCfgReq->Props, pThis->Cfg.msLatencyOut);
    685 
    686         DSLOG(("DSound: Playback buffer is %ld bytes\n", bd.dwBufferBytes));
     675        bd.dwFlags       = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE;
     676        bd.dwBufferBytes = DrvAudioHlpFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize);
     677
     678        DSLOG(("DSound: Requested playback buffer is %RU64ms (%ld bytes)\n",
     679               pCfgReq->Backend.cfBufferSize, DrvAudioHlpBytesToMs(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes));
    687680
    688681        hr = IDirectSound8_CreateSoundBuffer(pThis->pDS, &bd, &pDSB, NULL);
     
    724717            break;
    725718        }
     719
     720        DSLOG(("DSound: Acquired playback buffer is %RU64ms (%ld bytes)\n",
     721               DrvAudioHlpBytesToMs(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes));
    726722
    727723        DSLOG(("DSound: Acquired playback format:\n"
     
    756752        pStreamDS->cbBufSize = bc.dwBufferBytes;
    757753
    758         RTCritSectEnter(&pThis->CritSect);
    759 
    760         rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize * 2 /* Double buffering */);
     754        rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize) * 2; /* Use "double buffering" */
    761755        AssertRC(rc);
    762756
    763         /*
    764          * Install notification.
    765          */
    766         LPDIRECTSOUNDNOTIFY8 pNotify;
    767         hr = IDirectSoundNotify_QueryInterface(pStreamDS->Out.pDSB, IID_IDirectSoundNotify8, (PVOID *)&pNotify);
    768         if (SUCCEEDED(hr))
    769         {
    770             DSBPOSITIONNOTIFY dsPosNotify[3];
    771             RT_ZERO(dsPosNotify);
    772 
    773             dsPosNotify[0].dwOffset     = 0;
    774             dsPosNotify[0].hEventNotify = pThis->aEvents[DSOUNDEVENT_OUTPUT];
    775 
    776             dsPosNotify[1].dwOffset     = float(pStreamDS->cbBufSize * 0.3);
    777             dsPosNotify[1].hEventNotify = pThis->aEvents[DSOUNDEVENT_OUTPUT];
    778 
    779             dsPosNotify[2].dwOffset     = float(pStreamDS->cbBufSize * 0.6);
    780             dsPosNotify[2].hEventNotify = pThis->aEvents[DSOUNDEVENT_OUTPUT];
    781 
    782             hr = IDirectSoundNotify_SetNotificationPositions(pNotify, RT_ELEMENTS(dsPosNotify), dsPosNotify);
    783             if (FAILED(hr))
    784                 DSLOGREL(("DSound: Setting playback position notification failed with %Rhrc\n", hr));
    785 
    786             IDirectSoundNotify_Release(pNotify);
    787 
    788             pThis->pDSStrmOut = pStreamDS;
    789         }
    790         else
    791             DSLOGREL(("DSound: Querying interface for position notification failed with %Rhrc\n", hr));
    792 
    793         RTCritSectLeave(&pThis->CritSect);
    794 
    795         pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize);
     757        pThis->pDSStrmOut = pStreamDS;
     758
     759        pCfgAcq->Backend.cfBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize);
     760        pCfgAcq->Backend.cfPeriod     = pCfgAcq->Backend.cfBufferSize / 2;
     761        pCfgAcq->Backend.cfPreBuf     = pCfgAcq->Backend.cfBufferSize;
    796762
    797763    } while (0);
     
    807773    AssertPtrReturnVoid(pStreamDS);
    808774
    809     AssertPtr(pStreamDS->pCfg);
    810     PPDMAUDIOPCMPROPS pProps = &pStreamDS->pCfg->Props;
     775    PPDMAUDIOPCMPROPS pProps = &pStreamDS->Cfg.Props;
    811776
    812777    HRESULT hr = IDirectSoundBuffer_SetCurrentPosition(pStreamDS->Out.pDSB, 0 /* Position */);
     
    831796            DSLOGREL(("DSound: Re-fetching current position when clearing buffer failed with %Rhrc\n", hr));
    832797    }
     798}
     799
     800/**
     801 * Transfers audio data from the internal buffer to the DirectSound playback instance.
     802 * Due to internal accounting and querying DirectSound, this function knows how much it can transfer at once.
     803 *
     804 * @return  IPRT status code.
     805 * @param   pThis               Host audio driver instance.
     806 */
     807static int dsoundPlayTransfer(PDRVHOSTDSOUND pThis)
     808{
     809    PDSOUNDSTREAM pStreamDS = pThis->pDSStrmOut;
     810
     811    if (   !pStreamDS
     812        || !pStreamDS->fEnabled)
     813    {
     814        return VINF_SUCCESS;
     815    }
     816
     817    uint32_t cbTransferred = 0;
     818
     819    PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf;
     820    AssertPtr(pCircBuf);
     821
     822    LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB;
     823    AssertPtr(pDSB);
     824
     825    int rc = VINF_SUCCESS;
     826
     827    DWORD offPlayCursor, offWriteCursor;
     828    HRESULT hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &offPlayCursor, &offWriteCursor);
     829    if (FAILED(hr))
     830    {
     831        rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     832        return rc;
     833    }
     834
     835    DWORD cbFree, cbRemaining;
     836    if (pStreamDS->Out.fFirstTransfer)
     837    {
     838        cbRemaining = 0;
     839        cbFree      = pStreamDS->cbBufSize;
     840    }
     841    else
     842    {
     843        cbFree      = dsoundRingDistance(offPlayCursor, pStreamDS->Out.offWritePos, pStreamDS->cbBufSize);
     844        cbRemaining = dsoundRingDistance(pStreamDS->Out.offWritePos, offPlayCursor, pStreamDS->cbBufSize);
     845    }
     846
     847    uint32_t cbAvail      = (uint32_t)RTCircBufUsed(pCircBuf);
     848    uint32_t cbToTransfer = RT_MIN(cbFree, cbAvail);
     849
     850#ifdef LOG_ENABLED
     851    if (!pStreamDS->Dbg.tsLastTransferredMs)
     852        pStreamDS->Dbg.tsLastTransferredMs = RTTimeMilliTS();
     853    Log3Func(("tsLastTransferredMs=%RU64ms, cbAvail=%RU32, cbFree=%RU32 -> cbToTransfer=%RU32\n",
     854              RTTimeMilliTS() - pStreamDS->Dbg.tsLastTransferredMs, cbAvail, cbFree, cbToTransfer));
     855    pStreamDS->Dbg.tsLastTransferredMs = RTTimeMilliTS();
     856#endif
     857
     858#if 0
     859    if (cbToTransfer > cbAvail) /* Paranoia. */
     860        cbToTransfer = cbAvail;
     861
     862    if (cbToTransfer > cbFree)
     863        cbToTransfer = cbFree;
     864#endif
     865
     866    while (cbToTransfer)
     867    {
     868        DWORD cb1 = 0;
     869        DWORD cb2 = 0;
     870
     871        void  *pvBuf;
     872        size_t cbBuf;
     873        RTCircBufAcquireReadBlock(pCircBuf, cbToTransfer, &pvBuf, &cbBuf);
     874
     875        if (cbBuf)
     876        {
     877            PVOID pv1, pv2;
     878            hr = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, (DWORD)cbBuf,
     879                                     &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
     880            if (FAILED(hr))
     881            {
     882                rc = VERR_ACCESS_DENIED;
     883                break;
     884            }
     885
     886            AssertPtr(pv1);
     887            Assert(cb1);
     888
     889            memcpy(pv1, pvBuf, cb1);
     890
     891            if (pv2 && cb2) /* Buffer wrap-around? Write second part. */
     892                memcpy(pv2, (uint8_t *)pvBuf + cb1, cb2);
     893
     894            directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2);
     895
     896            pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cb1 + cb2) % pStreamDS->cbBufSize;
     897
     898            Assert(cbToTransfer >= cbBuf);
     899            cbToTransfer -= (uint32_t)cbBuf;
     900
     901            cbTransferred += cb1 + cb2;
     902        }
     903
     904        RTCircBufReleaseReadBlock(pCircBuf, cb1 + cb2);
     905    }
     906
     907    pStreamDS->Out.cbTransferred += cbTransferred;
     908
     909    if (   pStreamDS->Out.fFirstTransfer
     910        && pStreamDS->Out.cbTransferred >= DrvAudioHlpFramesToBytes(&pStreamDS->Cfg.Props, pStreamDS->Cfg.Backend.cfPreBuf))
     911    {
     912        hr = directSoundPlayStart(pThis, pStreamDS);
     913        if (SUCCEEDED(hr))
     914        {
     915            DSLOG(("DSound: Started playing output\n"));
     916            pStreamDS->Out.fFirstTransfer = false;
     917        }
     918        else
     919            rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     920    }
     921
     922    cbAvail = (uint32_t)RTCircBufUsed(pCircBuf);
     923    if (   !cbAvail
     924        && cbTransferred)
     925    {
     926        pStreamDS->Out.cbLastTransferred = cbTransferred;
     927        pStreamDS->Out.tsLastTransferred = RTTimeMilliTS();
     928    }
     929
     930    LogFlowFunc(("cbTransferred=%RU32, cbAvail=%RU32, rc=%Rrc\n", cbTransferred, cbAvail, rc));
     931    return rc;
    833932}
    834933
     
    9081007
    9091008
    910 static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
     1009/**
     1010 * Resets the state of a DirectSound stream.
     1011 *
     1012 * @return  HRESULT
     1013 * @param   pThis               Host audio driver instance.
     1014 * @param   pStreamDS           Stream to reset state for.
     1015 */
     1016static HRESULT directSoundPlayReset(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
    9111017{
    9121018    AssertPtrReturn(pThis,     E_POINTER);
     
    9191025     *       once enough audio output data is available. */
    9201026
    921     pStreamDS->Out.fFirstPlayback = true;
    922     pStreamDS->Out.cUnderruns     = 0;
    923 
    924     DSLOG(("DSound: Playback started\n"));
     1027    if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
     1028    {
     1029        pStreamDS->Out.fFirstTransfer = true;
     1030        pStreamDS->Out.cUnderruns     = 0;
     1031
     1032        pStreamDS->Out.cbLastTransferred = 0;
     1033        pStreamDS->Out.tsLastTransferred = 0;
     1034
     1035        pStreamDS->Out.cbTransferred = 0;
     1036        pStreamDS->Out.cbWritten = 0;
     1037
     1038        pStreamDS->Out.offPlayCursorLastPending = 0;
     1039        pStreamDS->Out.offPlayCursorLastPlayed = 0;
     1040    }
    9251041
    9261042    return S_OK;
    9271043}
     1044
     1045
     1046/**
     1047 * Starts playing a DirectSound stream.
     1048 *
     1049 * @return  HRESULT
     1050 * @param   pThis               Host audio driver instance.
     1051 * @param   pStreamDS           Stream to start playing.
     1052 */
     1053static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
     1054{
     1055    HRESULT hr = S_OK;
     1056
     1057    DWORD fFlags = DSCBSTART_LOOPING;
     1058
     1059    for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
     1060    {
     1061        hr = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, fFlags);
     1062        if (   SUCCEEDED(hr)
     1063            || hr != DSERR_BUFFERLOST)
     1064            break;
     1065        else
     1066        {
     1067            LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
     1068            directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
     1069        }
     1070    }
     1071
     1072    return hr;
     1073}
     1074
    9281075
    9291076/*
     
    11241271        bd.dwSize        = sizeof(bd);
    11251272        bd.lpwfxFormat   = &wfx;
    1126         bd.dwBufferBytes = DrvAudioHlpMsToBytes(&pCfgReq->Props, pThis->Cfg.msLatencyIn);
    1127 
    1128         DSLOG(("DSound: Capture buffer is %ld bytes\n", bd.dwBufferBytes));
     1273        bd.dwBufferBytes = DrvAudioHlpFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cfBufferSize);
     1274
     1275        DSLOG(("DSound: Requested capture buffer is %RU64ms (%ld bytes)\n",
     1276               pCfgReq->Backend.cfBufferSize, DrvAudioHlpBytesToMs(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes));
    11291277
    11301278        LPDIRECTSOUNDCAPTUREBUFFER pDSCB;
     
    11771325            break;
    11781326        }
     1327
     1328        DSLOG(("DSound: Acquired capture buffer is %RU64ms (%ld bytes)\n",
     1329               DrvAudioHlpBytesToMs(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes));
    11791330
    11801331        DSLOG(("DSound: Capture format:\n"
     
    12061357        pStreamDS->cbBufSize     = bc.dwBufferBytes;
    12071358
    1208         rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize * 2 /* Double buffering */);
     1359        rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize) * 2; /* Use "double buffering". */
    12091360        AssertRC(rc);
    12101361
    1211         /*
    1212          * Install notification.
    1213          */
    1214         LPDIRECTSOUNDNOTIFY8 pNotify;
    1215         hr = IDirectSoundNotify_QueryInterface(pStreamDS->In.pDSCB, IID_IDirectSoundNotify8, (PVOID *)&pNotify);
    1216         if (SUCCEEDED(hr))
    1217         {
    1218             DSBPOSITIONNOTIFY dsPosNotify[3];
    1219             RT_ZERO(dsPosNotify);
    1220 
    1221             dsPosNotify[0].dwOffset     = 0;
    1222             dsPosNotify[0].hEventNotify = pThis->aEvents[DSOUNDEVENT_INPUT];
    1223 
    1224             dsPosNotify[1].dwOffset     = float(pStreamDS->cbBufSize * 0.3);
    1225             dsPosNotify[1].hEventNotify = pThis->aEvents[DSOUNDEVENT_INPUT];
    1226 
    1227             dsPosNotify[2].dwOffset     = float(pStreamDS->cbBufSize * 0.6);
    1228             dsPosNotify[2].hEventNotify = pThis->aEvents[DSOUNDEVENT_INPUT];
    1229 
    1230             hr = IDirectSoundNotify_SetNotificationPositions(pNotify, 3 /* Count */, dsPosNotify);
    1231             if (FAILED(hr))
    1232                 DSLOGREL(("DSound: Setting capture position notification failed with %Rhrc\n", hr));
    1233 
    1234             IDirectSoundNotify_Release(pNotify);
    1235 
    1236             pThis->pDSStrmIn = pStreamDS;
    1237         }
    1238 
    1239         pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize);
     1362        pThis->pDSStrmIn = pStreamDS;
     1363
     1364        pCfgAcq->Backend.cfBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize);
    12401365
    12411366    } while (0);
     
    15461671    pStreamDS->Out.offPlayCursorLastPending = 0;
    15471672    pStreamDS->Out.cbWritten = 0;
    1548     pStreamDS->Out.cbPlayed = 0;
    1549     pStreamDS->Out.fFirstPlayback = true;
    1550     pStreamDS->Out.tsLastPlayMs = 0;
     1673    pStreamDS->Out.cbTransferred = 0;
     1674    pStreamDS->Out.fFirstTransfer = true;
     1675    pStreamDS->Out.cbLastTransferred = 0;
     1676    pStreamDS->Out.tsLastTransferred = 0;
    15511677
    15521678    int rc = VINF_SUCCESS;
     
    15721698        case PDMAUDIOSTREAMCMD_ENABLE:
    15731699        {
     1700            hr = directSoundPlayReset(pThis, pStreamDS);
     1701            if (FAILED(hr))
     1702                rc = VERR_NOT_SUPPORTED; /** @todo Fix this. */
     1703            break;
     1704        }
     1705
     1706        case PDMAUDIOSTREAMCMD_RESUME:
     1707        {
    15741708            hr = directSoundPlayStart(pThis, pStreamDS);
    15751709            if (FAILED(hr))
     
    15781712        }
    15791713
    1580         case PDMAUDIOSTREAMCMD_RESUME:
    1581         {
    1582             hr = directSoundPlayStart(pThis, pStreamDS);
    1583             if (SUCCEEDED(hr))
    1584             {
    1585                 BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_OUTPUT]);
    1586                 RT_NOREF(fRc);
    1587                 Assert(fRc);
    1588             }
    1589 
    1590             if (FAILED(hr))
    1591                 rc = VERR_NOT_SUPPORTED; /** @todo Fix this. */
    1592             break;
    1593         }
    1594 
    15951714        case PDMAUDIOSTREAMCMD_DISABLE:
    15961715        case PDMAUDIOSTREAMCMD_PAUSE:
     
    16021721        }
    16031722
     1723        case PDMAUDIOSTREAMCMD_DRAIN:
     1724        {
     1725            /* Make sure we transferred everything. */
     1726            rc = dsoundPlayTransfer(pThis);
     1727            if (   RT_SUCCESS(rc)
     1728                && pStreamDS->Out.fFirstTransfer)
     1729            {
     1730                /* If this was the first transfer ever for this stream, make sure to also play the (short) audio data. */
     1731                DSLOG(("DSound: Started playing output (short sound)\n"));
     1732
     1733                pStreamDS->Out.fFirstTransfer    = false;
     1734                pStreamDS->Out.cbLastTransferred = pStreamDS->Out.cbTransferred; /* All transferred audio data must be played. */
     1735                pStreamDS->Out.tsLastTransferred = RTTimeMilliTS();
     1736
     1737                hr = directSoundPlayStart(pThis, pStreamDS);
     1738                if (FAILED(hr))
     1739                    rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     1740            }
     1741            break;
     1742        }
     1743
    16041744        default:
    1605         {
    1606             AssertMsgFailed(("Invalid command: %ld\n", enmStreamCmd));
    1607             rc = VERR_INVALID_PARAMETER;
     1745            rc = VERR_NOT_SUPPORTED;
    16081746            break;
    1609         }
    16101747    }
    16111748
     
    16581795
    16591796    Assert(cbWrittenTotal <= cxBuf);
     1797    Assert(cbWrittenTotal == cxBuf);
    16601798
    16611799    pStreamDS->Out.cbWritten += cbWrittenTotal;
    16621800
    1663     if (   pStreamDS->Out.fFirstPlayback
    1664         && pStreamDS->Out.cbWritten >= pStreamDS->cbBufSize)
    1665     {
    1666         BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_OUTPUT]);
    1667         RT_NOREF(fRc);
    1668         Assert(fRc);
    1669     }
     1801    rc = dsoundPlayTransfer(pThis);
     1802    AssertRC(rc);
    16701803
    16711804    if (RT_SUCCESS(rc))
     
    17401873                {
    17411874                    PDMAUDIOSTREAMCFG CfgAcq;
    1742                     hr = directSoundCaptureOpen(pThis, pStreamDS, pStreamDS->pCfg /* pCfgReq */, &CfgAcq);
     1875                    hr = directSoundCaptureOpen(pThis, pStreamDS, &pStreamDS->Cfg /* pCfgReq */, &CfgAcq);
    17431876                    if (SUCCEEDED(hr))
    17441877                    {
    1745                         DrvAudioHlpStreamCfgFree(pStreamDS->pCfg);
    1746 
    1747                         pStreamDS->pCfg = DrvAudioHlpStreamCfgDup(&CfgAcq);
    1748                         AssertPtr(pStreamDS->pCfg);
     1878                        rc = DrvAudioHlpStreamCfgCopy(&pStreamDS->Cfg, &CfgAcq);
     1879                        if (RT_FAILURE(rc))
     1880                            break;
    17491881
    17501882                        /** @todo What to do if the format has changed? */
     
    17771909
    17781910        default:
    1779         {
    1780             AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
    1781             rc = VERR_INVALID_PARAMETER;
     1911            rc = VERR_NOT_SUPPORTED;
    17821912            break;
    1783         }
    17841913    }
    17851914
     
    18591988}
    18601989
    1861 static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown)
    1862 {
    1863     AssertPtrReturn(pThis, VERR_INVALID_POINTER);
    1864 
    1865     if (fShutdown)
    1866     {
    1867         LogFlowFunc(("Shutting down thread ...\n"));
    1868         pThis->fShutdown = fShutdown;
    1869     }
    1870 
    1871     /* Set the notification event so that the thread is being notified. */
    1872     BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
    1873     RT_NOREF(fRc);
    1874     Assert(fRc);
    1875 
    1876     return VINF_SUCCESS;
    1877 }
    1878 
    1879 
    1880 static DECLCALLBACK(int) dsoundThread(RTTHREAD hThreadSelf, void *pvUser)
    1881 {
    1882     PDRVHOSTDSOUND pThis = (PDRVHOSTDSOUND)pvUser;
    1883     AssertPtr(pThis);
     1990/**
     1991 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
     1992 */
     1993void drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
     1994{
     1995    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
    18841996
    18851997    LogFlowFuncEnter();
    18861998
    1887     /* Let caller know that we're done initializing, regardless of the result. */
    1888     int rc = RTThreadUserSignal(hThreadSelf);
    1889     AssertRC(rc);
    1890 
    1891     for (;;)
    1892     {
    1893         RTCritSectEnter(&pThis->CritSect);
    1894 
    1895         HANDLE aEvents[VBOX_DSOUND_MAX_EVENTS];
    1896         DWORD  cEvents = 0;
    1897         for (uint8_t i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++)
    1898         {
    1899             if (pThis->aEvents[i])
    1900                 aEvents[cEvents++] = pThis->aEvents[i];
    1901         }
    1902         Assert(cEvents);
    1903 
    1904         RTCritSectLeave(&pThis->CritSect);
    1905 
    1906         DWORD dwObj = WaitForMultipleObjects(cEvents, aEvents, FALSE /* bWaitAll */, INFINITE);
    1907 
    1908         RTCritSectEnter(&pThis->CritSect);
    1909 
    1910         switch (dwObj)
    1911         {
    1912             case WAIT_FAILED:
    1913             {
    1914                 rc = VERR_CANCELLED;
    1915                 break;
    1916             }
    1917 
    1918             case WAIT_TIMEOUT:
    1919             {
    1920                 rc = VERR_TIMEOUT;
    1921                 break;
    1922             }
    1923 
    1924             case WAIT_OBJECT_0:
    1925             case WAIT_OBJECT_0 + 1:
    1926             case WAIT_OBJECT_0 + 2:
    1927             case WAIT_OBJECT_0 + 3:
    1928             case WAIT_OBJECT_0 + 4:
    1929             {
    1930                 dwObj -= WAIT_OBJECT_0;
    1931 
    1932                 if (dwObj == DSOUNDEVENT_NOTIFY)
    1933                 {
    1934                     Log3Func(("Notify\n"));
    1935                 }
    1936                 else if (dwObj == DSOUNDEVENT_INPUT)
    1937                 {
    1938                     PDSOUNDSTREAM pStreamDS = pThis->pDSStrmIn;
    1939 
    1940                     if (   !pStreamDS
    1941                         || !pStreamDS->fEnabled)
    1942                     {
    1943                         Log3Func(("Skipping capture\n"));
    1944                         break;
    1945                     }
    1946 
    1947                     LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB = pStreamDS->In.pDSCB;
    1948                     AssertPtr(pDSCB);
    1949 
    1950                     DWORD offCaptureCursor;
    1951                     HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &offCaptureCursor);
    1952                     if (FAILED(hr))
    1953                         break;
    1954 
    1955                     DWORD cbUsed = dsoundRingDistance(offCaptureCursor, pStreamDS->In.offReadPos, pStreamDS->cbBufSize);
    1956 
    1957                     PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf;
    1958                     AssertPtr(pCircBuf);
    1959 
    1960                     uint32_t cbFree = (uint32_t)RTCircBufFree(pCircBuf);
    1961                     if (   !cbFree
    1962                         || pStreamDS->In.cOverruns < 32) /** @todo Make this configurable. */
    1963                     {
    1964                         DSLOG(("DSound: Warning: Capture buffer full, skipping to record data (%RU32 bytes)\n", cbUsed));
    1965                         pStreamDS->In.cOverruns++;
    1966                     }
    1967 
    1968                     DWORD cbToCapture = RT_MIN(cbUsed, cbFree);
    1969 
    1970                     Log3Func(("cbUsed=%ld, cbToCapture=%ld\n", cbUsed, cbToCapture));
    1971 
    1972                     while (cbToCapture)
    1973                     {
    1974                         void  *pvBuf;
    1975                         size_t cbBuf;
    1976                         RTCircBufAcquireWriteBlock(pCircBuf, cbToCapture, &pvBuf, &cbBuf);
    1977 
    1978                         if (cbBuf)
    1979                         {
    1980                             PVOID pv1, pv2;
    1981                             DWORD cb1, cb2;
    1982                             hr = directSoundCaptureLock(pStreamDS, pStreamDS->In.offReadPos, (DWORD)cbBuf,
    1983                                                         &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
    1984                             if (FAILED(hr))
    1985                                 break;
    1986 
    1987                             AssertPtr(pv1);
    1988                             Assert(cb1);
    1989 
    1990                             memcpy(pvBuf, pv1, cb1);
    1991 
    1992                             if (pv2 && cb2) /* Buffer wrap-around? Write second part. */
    1993                                 memcpy((uint8_t *)pvBuf + cb1, pv2, cb2);
    1994 
    1995                             directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
    1996 
    1997                             pStreamDS->In.offReadPos = (pStreamDS->In.offReadPos + cb1 + cb2) % pStreamDS->cbBufSize;
    1998 
    1999                             Assert(cbToCapture >= cbBuf);
    2000                             cbToCapture -= (uint32_t)cbBuf;
    2001                         }
    2002 
    2003 #ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
    2004                         if (cbBuf)
    2005                         {
    2006                             RTFILE fh;
    2007                             int rc2 = RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "dsoundCapture.pcm",
    2008                                                  RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
    2009                             if (RT_SUCCESS(rc2))
    2010                             {
    2011                                 RTFileWrite(fh, pvBuf, cbBuf, NULL);
    2012                                 RTFileClose(fh);
    2013                             }
    2014                         }
    2015 #endif
    2016                         RTCircBufReleaseWriteBlock(pCircBuf, cbBuf);
    2017                     }
    2018                 }
    2019                 else if (dwObj == DSOUNDEVENT_OUTPUT)
    2020                 {
    2021                     PDSOUNDSTREAM pStreamDS = pThis->pDSStrmOut;
    2022 
    2023                     if (   !pStreamDS
    2024                         || !pStreamDS->fEnabled)
    2025                     {
    2026                         Log3Func(("Skipping playback\n"));
    2027                         break;
    2028                     }
    2029 
    2030                     LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB;
    2031                     AssertPtr(pDSB);
    2032 
    2033                     HRESULT hr;
    2034 
    2035                     DWORD offPlayCursor, offWriteCursor;
    2036                     hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &offPlayCursor, &offWriteCursor);
    2037                     if (FAILED(hr))
    2038                         break;
    2039 
    2040                     DWORD cbFree, cbRemaining;
    2041                     if (pStreamDS->Out.fFirstPlayback)
    2042                     {
    2043                         cbRemaining = 0;
    2044                         cbFree      = dsoundRingDistance(pStreamDS->Out.offWritePos, 0, pStreamDS->cbBufSize);
    2045                     }
    2046                     else
    2047                     {
    2048                         cbFree      = dsoundRingDistance(offPlayCursor, pStreamDS->Out.offWritePos, pStreamDS->cbBufSize);
    2049                         cbRemaining = dsoundRingDistance(pStreamDS->Out.offWritePos, offPlayCursor, pStreamDS->cbBufSize);
    2050                     }
    2051 
    2052                     PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf;
    2053                     AssertPtr(pCircBuf);
    2054 
    2055                     DWORD cbUsed   = (uint32_t)RTCircBufUsed(pCircBuf);
    2056                     DWORD cbToPlay = RT_MIN(cbFree, cbUsed);
    2057 
    2058                     if (   !cbUsed
    2059                         && pStreamDS->Out.cbWritten
    2060                         && pStreamDS->Out.cUnderruns < 32) /** @todo Make this configurable. */
    2061                     {
    2062                         //DSLOG(("DSound: Warning: No more playback data available within time (%RU32 bytes free)\n", cbFree));
    2063                         Log3Func(("Underrun (cbFree=%ld, cbRemaining=%ld, cbUsed=%ld, cbToPlay=%ld, offPlay=%ld, offWrite=%ld)\n",
    2064                                   cbFree, cbRemaining, cbUsed, cbToPlay, offPlayCursor, offWriteCursor));
    2065                         pStreamDS->Out.cUnderruns++;
    2066                     }
    2067 
    2068                     while (cbToPlay)
    2069                     {
    2070                         void  *pvBuf;
    2071                         size_t cbBuf;
    2072                         RTCircBufAcquireReadBlock(pCircBuf, cbToPlay, &pvBuf, &cbBuf);
    2073 
    2074                         if (cbBuf)
    2075                         {
    2076                             PVOID pv1, pv2;
    2077                             DWORD cb1, cb2;
    2078                             hr = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, (DWORD)cbBuf,
    2079                                                      &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
    2080                             if (FAILED(hr))
    2081                                 break;
    2082 
    2083                             AssertPtr(pv1);
    2084                             Assert(cb1);
    2085 
    2086                             memcpy(pv1, pvBuf, cb1);
    2087 
    2088                             if (pv2 && cb2) /* Buffer wrap-around? Write second part. */
    2089                                 memcpy(pv2, (uint8_t *)pvBuf + cb1, cb2);
    2090 
    2091                             directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2);
    2092 
    2093                             pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cb1 + cb2) % pStreamDS->cbBufSize;
    2094 
    2095                             Assert(cbToPlay >= cbBuf);
    2096                             cbToPlay -= (uint32_t)cbBuf;
    2097 
    2098                             pStreamDS->Out.cbPlayed += cb1 + cb2;
    2099                         }
    2100 
    2101                         RTCircBufReleaseReadBlock(pCircBuf, cbBuf);
    2102                     }
    2103 
    2104                     if (   pStreamDS->Out.fFirstPlayback
    2105                         && RTCircBufUsed(pCircBuf) >= pStreamDS->cbBufSize)
    2106                     {
    2107                         DWORD fFlags = DSCBSTART_LOOPING;
    2108 
    2109                         for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
    2110                         {
    2111                             hr = IDirectSoundBuffer8_Play(pDSB, 0, 0, fFlags);
    2112                             if (   SUCCEEDED(hr)
    2113                                 || hr != DSERR_BUFFERLOST)
    2114                                 break;
    2115                             else
    2116                             {
    2117                                 LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
    2118                                 directSoundPlayRestore(pThis, pDSB);
    2119                             }
    2120                         }
    2121 
    2122                         if (SUCCEEDED(hr))
    2123                         {
    2124                             DSLOG(("DSound: Started playing output\n"));
    2125                             pStreamDS->Out.fFirstPlayback = false;
    2126                         }
    2127                     }
    2128                 }
    2129                 break;
    2130             }
    2131 
    2132             default:
    2133                 AssertFailed();
    2134                 break;
    2135         }
    2136 
    2137         RTCritSectLeave(&pThis->CritSect);
    2138 
    2139         if (pThis->fShutdown)
    2140             break;
    2141 
    2142     } /* for */
    2143 
    2144     pThis->fStopped = true;
    2145 
    2146     LogFlowFunc(("Exited with fShutdown=%RTbool, rc=%Rrc\n", pThis->fShutdown, rc));
    2147     return rc;
    2148 }
    2149 
    2150 /**
    2151  * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
    2152  */
    2153 void drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
    2154 {
    2155     PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
    2156 
    2157     LogFlowFuncEnter();
    2158 
    2159     int rc = dsoundNotifyThread(pThis, true /* fShutdown */);
    2160     AssertRC(rc);
    2161 
    2162     int rcThread;
    2163     rc = RTThreadWait(pThis->Thread,  15 * 1000 /* 15s timeout */, &rcThread);
    2164     LogFlowFunc(("rc=%Rrc, rcThread=%Rrc\n", rc, rcThread));
    2165 
    2166     Assert(pThis->fStopped);
    2167 
    2168     for (int i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++)
    2169     {
    2170         if (pThis->aEvents[i])
    2171             CloseHandle(pThis->aEvents[i]);
    2172     }
     1999    RT_NOREF(pThis);
    21732000
    21742001    LogFlowFuncLeave();
     
    21922019        IDirectSound_Release(pDirectSound);
    21932020
    2194         /* Create notification events. */
    2195         for (int i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++)
    2196         {
    2197             pThis->aEvents[i] = CreateEvent(NULL /* Security attribute */,
    2198                                             FALSE /* bManualReset */, FALSE /* bInitialState */,
    2199                                             NULL /* lpName */);
    2200             Assert(pThis->aEvents[i] != NULL);
    2201         }
    2202 
    2203         /* Start notification thread. */
    2204         rc = RTThreadCreate(&pThis->Thread, dsoundThread,
    2205                             pThis /*pvUser*/, 0 /*cbStack*/,
    2206                             RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "dsoundNtfy");
    2207         if (RT_SUCCESS(rc))
    2208         {
    2209             /* Wait for the thread to initialize. */
    2210             rc = RTThreadUserWait(pThis->Thread, RT_MS_1MIN);
    2211             if (RT_FAILURE(rc))
    2212                 DSLOGREL(("DSound: Waiting for thread to initialize failed with rc=%Rrc\n", rc));
    2213         }
    2214         else
    2215             DSLOGREL(("DSound: Creating thread failed with rc=%Rrc\n", rc));
     2021        rc = VINF_SUCCESS;
    22162022
    22172023        dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, DSOUNDENUMCBFLAGS_LOG /* fEnum */);
     
    22492055static int dsoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
    22502056{
    2251     CFGMR3QueryUIntDef(pCfg, "LatencyMsIn",  &pThis->Cfg.msLatencyIn,  DRV_DSOUND_DEFAULT_LATENCY_MS_IN);
    2252     CFGMR3QueryUIntDef(pCfg, "LatencyMsOut", &pThis->Cfg.msLatencyOut, DRV_DSOUND_DEFAULT_LATENCY_MS_OUT);
    2253 
    22542057    pThis->Cfg.pGuidPlay    = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->Cfg.uuidPlay);
    22552058    pThis->Cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn",  &pThis->Cfg.uuidCapture);
    22562059
    2257     DSLOG(("DSound: Configuration: Input latency = %ums, Output latency = %ums, DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
    2258            pThis->Cfg.msLatencyIn,
    2259            pThis->Cfg.msLatencyOut,
     2060    DSLOG(("DSound: Configuration: DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
    22602061           &pThis->Cfg.uuidPlay,
    22612062           &pThis->Cfg.uuidCapture));
     
    22972098    if (RT_SUCCESS(rc))
    22982099    {
    2299         pStreamDS->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
    2300         if (pStreamDS->pCfg)
    2301         {
     2100        rc = DrvAudioHlpStreamCfgCopy(&pStreamDS->Cfg, pCfgAcq);
     2101        if (RT_SUCCESS(rc))
    23022102            rc = RTCritSectInit(&pStreamDS->CritSect);
    2303         }
    2304         else
    2305             rc = VERR_NO_MEMORY;
    23062103    }
    23072104
     
    23202117    PDSOUNDSTREAM  pStreamDS = (PDSOUNDSTREAM)pStream;
    23212118
    2322     if (!pStreamDS->pCfg) /* Not (yet) configured? Skip. */
    2323         return VINF_SUCCESS;
    2324 
    23252119    int rc;
    2326     if (pStreamDS->pCfg->enmDir == PDMAUDIODIR_IN)
     2120    if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
    23272121        rc = dsoundDestroyStreamIn(pThis, pStreamDS);
    23282122    else
     
    23322126    {
    23332127        rc = RTCritSectDelete(&pStreamDS->CritSect);
    2334 
    2335         DrvAudioHlpStreamCfgFree(pStreamDS->pCfg);
    2336         pStreamDS->pCfg = NULL;
    23372128    }
    23382129
     
    23522143    PDSOUNDSTREAM  pStreamDS = (PDSOUNDSTREAM)pStream;
    23532144
    2354     if (!pStreamDS->pCfg) /* Not (yet) configured? Skip. */
    2355         return VINF_SUCCESS;
    2356 
    23572145    int rc;
    2358     if (pStreamDS->pCfg->enmDir == PDMAUDIODIR_IN)
     2146    if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
    23592147        rc = dsoundControlStreamIn(pThis,  pStreamDS, enmStreamCmd);
    23602148    else
     
    24092197    PDSOUNDSTREAM  pStreamDS = (PDSOUNDSTREAM)pStream;
    24102198
    2411     if (pStreamDS->pCfg->enmDir == PDMAUDIODIR_OUT)
     2199    if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
    24122200    {
    24132201        uint32_t cbPending = 0;
     
    24212209        if (!cbPending)
    24222210        {
    2423             const uint64_t tsNowMs = RTTimeMilliTS();
    2424             if (pStreamDS->Out.tsLastPlayMs == 0)
    2425                 pStreamDS->Out.tsLastPlayMs = tsNowMs;
    2426 
    2427             PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
    2428 
    2429             const uint64_t diffLastPlayMs = tsNowMs - pStreamDS->Out.tsLastPlayMs;
    2430             const uint64_t msThreshold    = pThis->Cfg.msLatencyOut;
    2431 
    2432             Log2Func(("diffLastPlayMs=%RU64ms\n", diffLastPlayMs));
    2433 
    2434             cbPending = (diffLastPlayMs >= msThreshold) ? 0 : 1;
    2435         }
     2211            const uint64_t diffLastTransferredMs  = RTTimeMilliTS() - pStreamDS->Out.tsLastTransferred;
     2212            const uint64_t uLastTranserredChunkMs = DrvAudioHlpBytesToMs(&pStreamDS->Cfg.Props, pStreamDS->Out.cbLastTransferred);
     2213            if (   uLastTranserredChunkMs
     2214                && diffLastTransferredMs < uLastTranserredChunkMs)
     2215                cbPending = 1;
     2216
     2217            Log3Func(("diffLastTransferredMs=%RU64ms, uLastTranserredChunkMs=%RU64ms (%RU32 bytes) -> cbPending=%RU32\n",
     2218                      diffLastTransferredMs, uLastTranserredChunkMs, pStreamDS->Out.cbLastTransferred, cbPending));
     2219        }
     2220        else
     2221            Log3Func(("cbPending=%RU32\n", cbPending));
    24362222
    24372223        return cbPending;
     
    24522238    PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
    24532239
    2454     if (!pStreamDS->pCfg) /* Not (yet) configured? Skip. */
    2455         return PDMAUDIOSTREAMSTS_FLAG_NONE;
    2456 
    24572240    PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
    2458     if (pStreamDS->pCfg->enmDir == PDMAUDIODIR_IN)
     2241    if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
    24592242    {
    24602243        if (pStreamDS->fEnabled)
     
    26232406    pThis->fEnabledIn  = false;
    26242407    pThis->fEnabledOut = false;
    2625     pThis->fStopped    = false;
    2626     pThis->fShutdown   = false;
    2627 
    2628     RT_ZERO(pThis->aEvents);
    26292408
    26302409    int rc = VINF_SUCCESS;
  • trunk/src/VBox/Devices/Audio/DrvHostDebugAudio.cpp

    r73350 r73370  
    120120
    121121    if (pCfgAcq)
    122         pCfgAcq->cFrameBufferHint = _1K;
     122    {
     123        pCfgAcq->Backend.cfPeriod     = DrvAudioHlpMsToFrames(&pCfgReq->Props, 1000 /* ms */);
     124        pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */
     125    }
    123126
    124127    return VINF_SUCCESS;
     
    159162    {
    160163        if (pCfgAcq)
    161             pCfgAcq->cFrameBufferHint = _1K;
     164        {
     165            pCfgAcq->Backend.cfPeriod     = DrvAudioHlpBytesToFrames(&pCfgReq->Props, 50 /* ms */);
     166            pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */
     167        }
    162168    }
    163169
  • trunk/src/VBox/Devices/Audio/DrvHostNullAudio.cpp

    r69119 r73370  
    174174static int nullCreateStreamIn(PNULLAUDIOSTREAM pStreamNull, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    175175{
    176     RT_NOREF(pStreamNull, pCfgReq);
    177 
    178     if (pCfgAcq)
    179         pCfgAcq->cFrameBufferHint = _1K;
     176    RT_NOREF(pStreamNull, pCfgReq, pCfgAcq);
    180177
    181178    return VINF_SUCCESS;
     
    185182static int nullCreateStreamOut(PNULLAUDIOSTREAM pStreamNull, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    186183{
    187     RT_NOREF(pStreamNull, pCfgReq);
    188 
    189     if (pCfgAcq)
    190         pCfgAcq->cFrameBufferHint = _1K; /** @todo Make this configurable. */
     184    RT_NOREF(pStreamNull, pCfgReq, pCfgAcq);
    191185
    192186    return VINF_SUCCESS;
  • trunk/src/VBox/Devices/Audio/DrvHostOSSAudio.cpp

    r73097 r73370  
    386386
    387387        default:
    388             AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
    389             rc = VERR_INVALID_PARAMETER;
     388            rc = VERR_NOT_SUPPORTED;
    390389            break;
    391390    }
     
    650649            }
    651650
    652             uint32_t cSamples = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, ossAcq.cFragments * ossAcq.cbFragmentSize);
    653             if (!cSamples)
    654                 rc = VERR_INVALID_PARAMETER;
    655 
    656651            if (RT_SUCCESS(rc))
    657652            {
    658                 size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, cSamples);
     653                size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, ossAcq.cFragments * ossAcq.cbFragmentSize);
    659654                void  *pvBuf = RTMemAlloc(cbBuf);
    660655                if (!pvBuf)
     
    668663                pStreamOSS->cbBuf = cbBuf;
    669664
    670                 pCfgAcq->cFrameBufferHint = cSamples;
     665                pCfgAcq->Backend.cfPeriod     = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, ossAcq.cbFragmentSize);
     666                pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */
     667                /** @todo Pre-buffering required? */
    671668            }
    672669        }
     
    689686    do
    690687    {
    691         uint32_t cSamples;
    692 
    693688        OSSAUDIOSTREAMCFG reqStream, obtStream;
    694689
     
    703698            memcpy(&pCfgAcq->Props, &obtStream.Props, sizeof(PDMAUDIOPCMPROPS));
    704699
    705             cSamples = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, obtStream.cFragments * obtStream.cbFragmentSize);
    706 
    707700            if (obtStream.cFragments * obtStream.cbFragmentSize & pStreamOSS->uAlign)
    708701            {
     
    716709            pStreamOSS->Out.fMMIO = false;
    717710
    718             size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, cSamples);
     711            size_t cbBuf = PDMAUDIOSTREAMCFG_F2B(pCfgAcq, obtStream.cFragments * obtStream.cbFragmentSize);
    719712            Assert(cbBuf);
    720713
     
    773766                if (!pvBuf)
    774767                {
    775                     LogRel(("OSS: Failed allocating playback buffer with %RU32 samples (%zu bytes)\n", cSamples, cbBuf));
     768                    LogRel(("OSS: Failed allocating playback buffer with %zu bytes\n", cbBuf));
    776769                    rc = VERR_NO_MEMORY;
    777770                    break;
     
    784777            }
    785778#endif
    786             pCfgAcq->cFrameBufferHint = cSamples;
     779            pCfgAcq->Backend.cfPeriod     = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, obtStream.cbFragmentSize);
     780            pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering" */
    787781        }
    788782
  • trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp

    r73097 r73370  
    788788    if (cbBuf)
    789789    {
    790         pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, cbBuf);
     790        pCfgAcq->Backend.cfPeriod     = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.minreq);
     791        pCfgAcq->Backend.cfBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.tlength);
     792        pCfgAcq->Backend.cfPreBuf     = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.prebuf);
    791793
    792794        pStreamPA->pDrv = pThis;
     
    833835    pCfgAcq->Props.uHz         = pStreamPA->SampleSpec.rate;
    834836    pCfgAcq->Props.cChannels   = pStreamPA->SampleSpec.channels;
    835     pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq,
    836                                                        RT_MIN(pStreamPA->BufAttr.fragsize * 10, pStreamPA->BufAttr.maxlength));
     837
     838    pCfgAcq->Backend.cfPeriod     = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.fragsize);
     839    pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use double buffering. */
    837840
    838841    LogFlowFuncLeaveRC(rc);
     
    13321335
    13331336        default:
    1334             AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
    1335             rc = VERR_INVALID_PARAMETER;
     1337            rc = VERR_NOT_SUPPORTED;
    13361338            break;
    13371339    }
     
    13751377
    13761378        default:
    1377             AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
    1378             rc = VERR_INVALID_PARAMETER;
     1379            rc = VERR_NOT_SUPPORTED;
    13791380            break;
    13801381    }
  • trunk/src/VBox/Devices/Audio/DrvHostValidationKit.cpp

    r70493 r73370  
    132132
    133133    if (pCfgAcq)
    134         pCfgAcq->cFrameBufferHint = _1K;
     134        pCfgAcq->cfPeriod = _1K;
    135135
    136136    return VINF_SUCCESS;
     
    194194    {
    195195        if (pCfgAcq)
    196             pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDbg->Out.cbPlayBuffer);
     196            pCfgAcq->cfPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDbg->Out.cbPlayBuffer);
    197197    }
    198198
  • trunk/src/VBox/Devices/Audio/HDAStream.cpp

    r73241 r73370  
    197197    /* Set scheduling hint (if available). */
    198198    if (pThis->u16TimerHz)
    199         pCfg->Device.uSchedulingHintMs = 1000 /*ms */ / pThis->u16TimerHz;
     199        pCfg->Device.uSchedulingHintMs = 1000 /* ms */ / pThis->u16TimerHz;
    200200
    201201    /* (Re-)Allocate the stream's internal DMA buffer, based on the PCM  properties we just got above. */
  • trunk/src/VBox/Devices/Audio/HDAStream.h

    r73244 r73370  
    165165     *  Should match SDFMT. */
    166166    PDMAUDIOSTREAMCFG       Cfg;
     167    uint32_t                Padding3;
    167168#ifdef HDA_USE_DMA_ACCESS_HANDLER
    168169    /** List of DMA handlers. */
     
    175176    uint16_t                cbDMALeft;
    176177    /** Unused, padding. */
    177     uint8_t                 abPadding3[2+4];
     178    uint8_t                 abPadding4[2+4];
    178179} HDASTREAMSTATE;
    179180AssertCompileSizeAlignment(HDASTREAMSTATE, 8);
  • trunk/src/VBox/Main/include/DrvAudioVRDE.h

    r70644 r73370  
    4747public:
    4848
     49    void onVRDEClientConnect(uint32_t uClientID);
     50    void onVRDEClientDisconnect(uint32_t uClientID);
    4951    int onVRDEControl(bool fEnable, uint32_t uFlags);
    5052    int onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin);
  • trunk/src/VBox/Main/src-client/ConsoleVRDPServer.cpp

    r73097 r73370  
    933933DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackClientConnect(void *pvCallback, uint32_t u32ClientId)
    934934{
    935     ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
    936 
    937     server->mConsole->i_VRDPClientConnect(u32ClientId);
     935    ConsoleVRDPServer *pServer = static_cast<ConsoleVRDPServer*>(pvCallback);
     936
     937    pServer->mConsole->i_VRDPClientConnect(u32ClientId);
    938938
    939939    /* Should the server report usage of an interface for each client?
    940940     * Similar to Intercept.
    941941     */
    942     int c = ASMAtomicIncS32(&server->mcClients);
     942    int c = ASMAtomicIncS32(&pServer->mcClients);
    943943    if (c == 1)
    944944    {
    945945        /* Features which should be enabled only if there is a client. */
    946         server->remote3DRedirect(true);
    947     }
     946        pServer->remote3DRedirect(true);
     947    }
     948
     949#ifdef VBOX_WITH_AUDIO_VRDE
     950    AudioVRDE *pVRDE = pServer->mConsole->i_getAudioVRDE();
     951    if (pVRDE)
     952        pVRDE->onVRDEClientConnect(u32ClientId);
     953#endif
    948954}
    949955
     
    964970        AudioVRDE *pVRDE = pServer->mConsole->i_getAudioVRDE();
    965971        if (pVRDE)
     972        {
    966973            pVRDE->onVRDEInputIntercept(false /* fIntercept */);
     974            pVRDE->onVRDEClientDisconnect(u32ClientId);
     975        }
    967976#endif
    968977    }
  • trunk/src/VBox/Main/src-client/DrvAudioVRDE.cpp

    r70644 r73370  
    55
    66/*
    7  * Copyright (C) 2013-2017 Oracle Corporation
     7 * Copyright (C) 2013-2018 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    5959    /** Pointer to the DrvAudio port interface that is above us. */
    6060    PPDMIAUDIOCONNECTOR  pDrvAudio;
     61    /** Number of connected clients to this VRDE instance. */
     62    uint32_t             cClients;
    6163} DRVAUDIOVRDE, *PDRVAUDIOVRDE;
    6264
     
    6971        struct
    7072        {
    71             /** Number of audio frames this stream can handle at once. */
    72             uint32_t    cfMax;
    7373            /** Circular buffer for holding the recorded audio frames from the host. */
    7474            PRTCIRCBUF  pCircBuf;
     
    7676        struct
    7777        {
    78             /** Timestamp (in virtual time ticks) of the last audio playback (of the VRDP server). */
    79             uint64_t    ticksPlayedLast;
    80             /** Internal counter (in audio frames) to track if and how much we can write to the VRDP server
    81              *  for the current time period. */
    82             uint64_t    cfToWrite;
     78            uint32_t last;
    8379        } Out;
    8480    };
     
    9086static int vrdeCreateStreamIn(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    9187{
    92     pStreamVRDE->In.cfMax = _1K; /** @todo Make this configurable. */
    93 
    94     int rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, pStreamVRDE->In.cfMax * (pCfgReq->Props.cBits / 8) /* Bytes */);
     88    RT_NOREF(pCfgReq);
     89    AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
     90
     91    pCfgAcq->Props.uHz       = 22050; /* The VRDP server's internal frequency. */
     92    pCfgAcq->Props.cChannels = 2;
     93    pCfgAcq->Props.cBits     = 16;
     94    pCfgAcq->Props.fSigned   = true;
     95
     96    /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
     97    const uint32_t cfVRDPServer = DrvAudioHlpMsToFrames(&pCfgAcq->Props, 200  /* ms */);
     98
     99    int rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, DrvAudioHlpFramesToBytes(&pCfgAcq->Props, cfVRDPServer));
    95100    if (RT_SUCCESS(rc))
    96     {
    97         if (pCfgAcq)
    98         {
    99             /*
    100              * Because of historical reasons the VRDP server operates on st_sample_t structures internally,
    101              * which is 2 * int64_t for left/right (stereo) channels.
    102              *
    103              * As the audio connector also uses this format, set the layout to "raw" and just let pass through
    104              * the data without any layout modification needed.
    105              */
    106             pCfgAcq->enmLayout        = PDMAUDIOSTREAMLAYOUT_RAW;
    107             pCfgAcq->cFrameBufferHint = pStreamVRDE->In.cfMax;
    108         }
    109     }
    110 
    111     return rc;
    112 }
    113 
    114 
    115 static int vrdeCreateStreamOut(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    116 {
    117     RT_NOREF(pStreamVRDE, pCfgReq);
    118 
    119     if (pCfgAcq)
    120101    {
    121102        /*
     
    126107         * the data without any layout modification needed.
    127108         */
    128         pCfgAcq->enmLayout         = PDMAUDIOSTREAMLAYOUT_RAW;
    129         pCfgAcq->cFrameBufferHint = _4K; /** @todo Make this configurable. */
     109        pCfgAcq->enmLayout        = PDMAUDIOSTREAMLAYOUT_RAW;
     110
     111        pCfgAcq->Backend.cfPeriod     = cfVRDPServer;
     112        pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */
     113        pCfgAcq->Backend.cfPreBuf     = pCfgAcq->Backend.cfPeriod;
     114    }
     115
     116    return rc;
     117}
     118
     119
     120static int vrdeCreateStreamOut(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     121{
     122    RT_NOREF(pStreamVRDE, pCfgReq);
     123
     124    if (pCfgAcq)
     125    {
     126        /*
     127         * Because of historical reasons the VRDP server operates on st_sample_t structures internally,
     128         * which is 2 * int64_t for left/right (stereo) channels.
     129         *
     130         * As the audio connector also uses this format, set the layout to "raw" and just let pass through
     131         * the data without any layout modification needed.
     132         */
     133        pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;
     134
     135        pCfgAcq->Props.uHz       = 22050; /* The VRDP server's internal frequency. */
     136        pCfgAcq->Props.cChannels = 2;
     137        pCfgAcq->Props.cBits     = 16;
     138        pCfgAcq->Props.fSigned   = true;
     139        pCfgAcq->Props.cShift    = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cBits, pCfgAcq->Props.cChannels);
     140
     141        /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
     142        pCfgAcq->Backend.cfPeriod     = DrvAudioHlpMsToFrames(&pCfgAcq->Props, 10  /* ms */);
     143        pCfgAcq->Backend.cfBufferSize = DrvAudioHlpMsToFrames(&pCfgAcq->Props, 200  /* ms */);
     144        pCfgAcq->Backend.cfPreBuf     = pCfgAcq->Backend.cfBufferSize;
    130145    }
    131146
     
    158173        case PDMAUDIOSTREAMCMD_ENABLE:
    159174        {
    160             rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE, pStreamVRDE->In.cfMax,
     175            rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
     176                                                               DrvAudioHlpMsToFrames(&pStreamVRDE->pCfg->Props, 200 /* ms */),
    161177                                                               pStreamVRDE->pCfg->Props.uHz, pStreamVRDE->pCfg->Props.cChannels,
    162178                                                               pStreamVRDE->pCfg->Props.cBits);
     
    280296                                                 pProps->fSigned);
    281297
     298    const uint64_t ticksNow     = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
     299    const uint64_t ticksElapsed = ticksNow  - pStreamVRDE->Out.last;
     300    const uint64_t ticksPerSec  = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
     301
     302    /* Remember when frames were consumed. */
     303    pStreamVRDE->Out.last = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
     304
     305    Log3Func(("FOOO -- %d\n", (int)((2 * ticksElapsed * pProps->uHz + ticksPerSec) / ticksPerSec / 2)));
     306
    282307    /* Use the internal counter to track if we (still) can write to the VRDP server
    283308     * or if we need to wait another round (time slot). */
    284     uint32_t cfToWrite = pStreamVRDE->Out.cfToWrite;
     309    uint32_t cfToWrite = cfLive; //pStreamVRDE->Out.cfToWrite;
    285310
    286311    Log3Func(("cfLive=%RU32, cfToWrite=%RU32\n", cfLive, cfToWrite));
     
    319344    if (RT_SUCCESS(rc))
    320345    {
    321         /* Subtract written frames from the counter. */
    322         Assert(pStreamVRDE->Out.cfToWrite >= cfWritten);
    323         pStreamVRDE->Out.cfToWrite      -= cfWritten;
    324 
    325         /* Remember when frames were consumed. */
    326         pStreamVRDE->Out.ticksPlayedLast = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
    327 
    328346        /* Return frames instead of bytes here
    329347         * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
     
    394412static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVRDEGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
    395413{
     414    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
     415    AssertPtrReturn(pDrv, PDMAUDIOBACKENDSTS_ERROR);
     416
    396417    RT_NOREF(enmDir);
    397     AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
    398418
    399419    return PDMAUDIOBACKENDSTS_RUNNING;
     
    516536    PVRDESTREAM   pStreamVRDE = (PVRDESTREAM)pStream;
    517537
    518     const uint64_t ticksNow     = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
    519     const uint64_t ticksElapsed = ticksNow  - pStreamVRDE->Out.ticksPlayedLast;
    520     const uint64_t ticksPerSec  = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
    521 
    522538    PPDMAUDIOPCMPROPS pProps  = &pStreamVRDE->pCfg->Props;
    523539
    524     /* Minimize the rounding error: frames = int((ticks * freq) / ticks_per_second + 0.5). */
    525     pStreamVRDE->Out.cfToWrite = (int)((2 * ticksElapsed * pProps->uHz + ticksPerSec) / ticksPerSec / 2);
     540    RT_NOREF(pDrv,  pProps);
    526541
    527542    /* Return frames instead of bytes here
    528543     * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
    529     return pStreamVRDE->Out.cfToWrite;
     544    return _16K; // pStreamVRDE->Out.cfToWrite;
    530545}
    531546
     
    536551static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioVRDEStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    537552{
    538     RT_NOREF(pInterface, pStream);
    539 
    540     return (PDMAUDIOSTREAMSTS_FLAG_INITIALIZED | PDMAUDIOSTREAMSTS_FLAG_ENABLED);
     553    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
     554    RT_NOREF(pStream);
     555
     556    PDMAUDIOSTREAMSTS streamSts = PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
     557
     558    if (pDrv->cClients) /* If any clients are connected, flag the stream as enabled. */
     559      streamSts |= PDMAUDIOSTREAMSTS_FLAG_ENABLED;
     560
     561    return streamSts;
    541562}
    542563
     
    595616
    596617    return VINF_SUCCESS;
     618}
     619
     620
     621void AudioVRDE::onVRDEClientConnect(uint32_t uClientID)
     622{
     623    RT_NOREF(uClientID);
     624
     625    LogRel2(("Audio: VRDE client connected\n"));
     626    mpDrv->cClients++;
     627}
     628
     629
     630void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID)
     631{
     632    RT_NOREF(uClientID);
     633
     634    LogRel2(("Audio: VRDE client disconnected\n"));
     635    Assert(mpDrv->cClients);
     636    mpDrv->cClients--;
    597637}
    598638
     
    719759    /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
    720760    pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
     761    pThis->cClients = 0;
    721762
    722763    /*
  • trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp

    r73104 r73370  
    498498        AssertFailed();
    499499
    500         if (pCfgAcq)
    501             pCfgAcq->cFrameBufferHint = 0;
    502 
    503500        LogRel2(("VideoRec: Support for surround audio not implemented yet\n"));
    504501        return VERR_NOT_SUPPORTED;
     
    525522            pCfgAcq->Props.uHz         = pSink->Codec.Parms.uHz;
    526523            pCfgAcq->Props.cShift      = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cBits, pCfgAcq->Props.cChannels);
    527             pCfgAcq->cFrameBufferHint = _4K; /** @todo Make this configurable. */
     524
     525            /* Every Opus frame marks a period for now. Optimize this later. */
     526            pCfgAcq->Backend.cfPeriod  = DrvAudioHlpMsToFrames(&pCfgAcq->Props, pSink->Codec.Opus.msFrame); /** @todo Make this configurable. */
    528527        }
    529528    }
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