VirtualBox

Changeset 88991 in vbox for trunk/src/VBox/Devices/Audio


Ignore:
Timestamp:
May 12, 2021 12:46:35 AM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
144329
Message:

Audio: Worked over draining, starting with the internal DMA buffer (instead of just the pre-buffer and backend buffer) and using the async I/O thread to keep calling PDMIAUDIOCONNECTOR::pfnStreamIterate and PDMIHOSTAUDIO::pfnStreamPlay (NULL buffer) every so often till the draining is done. Also put a rough deadline on the draining. The PDMAUDIOSTREAMCMD_DISABLE is now defined to stop playback/capturing immediately, even when already draining (if possible). This gets rid of the timers in DrvAudio and windows backends. DrvAudio no longer issue an DISABLE command at the end of the drain, it assumes the backend does that internally itself. After issuing PDMAUDIOSTREAMCMD_DRAIN the client (be it mixer or drvaudio) will not provide any more data for the buffers via pfnStreamPlay. Only tested windows, needs to re-test all platforms. bugref:9890

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

Legend:

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

    r88957 r88991  
    105105static int audioMixerSinkInit(PAUDMIXSINK pSink, PAUDIOMIXER pMixer, const char *pcszName, PDMAUDIODIR enmDir, PPDMDEVINS pDevIns);
    106106static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink, PPDMDEVINS pDevIns);
    107 static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster);
     107static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, PCPDMAUDIOVOLUME pVolMaster);
    108108static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink);
    109109static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
     
    117117
    118118/** size of output buffer for dbgAudioMixerSinkStatusToStr.   */
    119 #define AUDIOMIXERSINK_STATUS_STR_MAX sizeof("RUNNING PENDING_DISABLE DIRTY 0x12345678")
     119#define AUDIOMIXERSINK_STATUS_STR_MAX sizeof("RUNNING DRAINING DRAINED_DMA DRAINED_MIXBUF DIRTY 0x12345678")
    120120
    121121/**
     
    138138    } s_aFlags[] =
    139139    {
    140         { RT_STR_TUPLE("RUNNING "),          AUDMIXSINK_STS_RUNNING },
    141         { RT_STR_TUPLE("PENDING_DISABLE "),  AUDMIXSINK_STS_PENDING_DISABLE },
    142         { RT_STR_TUPLE("DIRTY "),            AUDMIXSINK_STS_DIRTY },
     140        { RT_STR_TUPLE("RUNNING "),         AUDMIXSINK_STS_RUNNING },
     141        { RT_STR_TUPLE("DRAINING "),        AUDMIXSINK_STS_DRAINING },
     142        { RT_STR_TUPLE("DRAINED_DMA"),      AUDMIXSINK_STS_DRAINED_DMA },
     143        { RT_STR_TUPLE("DRAINED_MIXBUF"),   AUDMIXSINK_STS_DRAINED_MIXBUF },
     144        { RT_STR_TUPLE("DIRTY "),           AUDMIXSINK_STS_DIRTY },
    143145    };
    144146    char *psz = pszDst;
     
    248250
    249251            pMixer->fFlags = fFlags;
     252            pMixer->uMagic = AUDIOMIXER_MAGIC;
    250253
    251254            if (pMixer->fFlags & AUDMIXER_FLAGS_DEBUG)
     
    282285{
    283286    RT_NOREF(pszArgs);
     287    Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
     288
     289    int rc2 = RTCritSectEnter(&pMixer->CritSect);
     290    AssertRCReturnVoid(rc2);
     291
     292    pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
     293                    pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
     294
    284295    PAUDMIXSINK pSink;
    285296    unsigned    iSink = 0;
    286 
    287     int rc2 = RTCritSectEnter(&pMixer->CritSect);
    288     if (RT_FAILURE(rc2))
    289         return;
    290 
    291     pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
    292                     pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
    293 
    294297    RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
    295298    {
     
    313316    if (!pMixer)
    314317        return;
     318    AssertPtr(pMixer);
     319    Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
    315320
    316321    int rc2 = RTCritSectEnter(&pMixer->CritSect);
     
    318323
    319324    LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
     325    pMixer->uMagic = AUDIOMIXER_MAGIC_DEAD;
    320326
    321327    PAUDMIXSINK pSink, pSinkNext;
     
    347353 * @param   pMixer              Mixer to invalidate data for.
    348354 */
    349 int audioMixerInvalidateInternal(PAUDIOMIXER pMixer)
     355static int audioMixerInvalidateInternal(PAUDIOMIXER pMixer)
    350356{
    351357    AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
    352 
    353358    LogFlowFunc(("[%s]\n", pMixer->pszName));
    354359
     
    374379{
    375380    AssertPtrReturnVoid(pMixer);
     381    Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
    376382
    377383    int rc2 = RTCritSectEnter(&pMixer->CritSect);
     
    453459{
    454460    AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
     461    Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
    455462    AssertPtrReturn(pVol,   VERR_INVALID_POINTER);
    456463
    457464    int rc = RTCritSectEnter(&pMixer->CritSect);
    458     if (RT_FAILURE(rc))
    459         return rc;
     465    AssertRCReturn(rc, rc);
    460466
    461467    memcpy(&pMixer->VolMaster, pVol, sizeof(PDMAUDIOVOLUME));
     
    478484*********************************************************************************************************************************/
    479485
     486#ifdef VBOX_STRICT
     487/**
     488 * Checks if @a pNeedle is in the list of streams associated with @a pSink.
     489 * @returns true / false.
     490 */
     491static bool audioMixerSinkIsStreamInList(PAUDMIXSINK pSink, PAUDMIXSTREAM pNeedle)
     492{
     493    PAUDMIXSTREAM pStream;
     494    RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
     495    {
     496        if (pStream == pNeedle)
     497            return true;
     498    }
     499    return false;
     500}
     501#endif /* VBOX_STRICT */
     502
    480503/**
    481504 * Adds an audio stream to a specific audio sink.
     
    489512    LogFlowFuncEnter();
    490513    AssertPtrReturn(pSink,   VERR_INVALID_POINTER);
     514    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    491515    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     516    Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC);
    492517    AssertPtrReturn(pStream->pConn, VERR_AUDIO_STREAM_NOT_READY);
    493518    AssertReturn(pStream->pSink == NULL, VERR_ALREADY_EXISTS);
     
    504529     */
    505530    if (    (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
    506         && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
     531        && !(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
    507532    {
    508533        audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE);
     
    544569{
    545570    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     571    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    546572    AssertPtrReturn(pConn, VERR_INVALID_POINTER);
    547573    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
    548574    AssertPtrNullReturn(ppStream, VERR_INVALID_POINTER);
    549     RT_NOREF(pDevIns); /* we'll probably be adding more statistics */
     575    Assert(pSink->AIO.pDevIns == pDevIns); RT_NOREF(pDevIns); /* we'll probably be adding more statistics */
    550576
    551577    /*
     
    623649                if (RT_SUCCESS(rc))
    624650                {
     651                    pMixStream->cFramesBackendBuffer = CfgHost.Backend.cFramesBufferSize;
     652
    625653                    /* Set up the mixing buffer conversion state. */
    626654                    if (pSink->enmDir == PDMAUDIODIR_OUT)
     
    632660
    633661                        /* Increase the stream's reference count to let others know
    634                          * we're reyling on it to be around now. */
     662                         * we're relying on it to be around now. */
    635663                        pConn->pfnStreamRetain(pConn, pStream);
    636                         pMixStream->pConn = pConn;
     664                        pMixStream->pConn  = pConn;
     665                        pMixStream->uMagic = AUDMIXSTREAM_MAGIC;
    637666
    638667                        RTCritSectLeave(&pSink->CritSect);
     
    666695}
    667696
    668 /**
    669  * Enables or disables a mixer sink.
    670  *
    671  * @returns VBox status code.
    672  *          Generally always VINF_SUCCESS unless the input is invalid.
    673  *          Individual driver errors are suppressed and ignored.
     697
     698/**
     699 * Starts playback/capturing on the mixer sink.
     700 *
     701 * @returns VBox status code.  Generally always VINF_SUCCESS unless the input
     702 *          is invalid.  Individual driver errors are suppressed and ignored.
    674703 * @param   pSink       Mixer sink to control.
    675  * @param   fEnable     Whether to enable or disable the sink.
    676  */
    677 int AudioMixerSinkEnable(PAUDMIXSINK pSink, bool fEnable)
     704 */
     705int AudioMixerSinkStart(PAUDMIXSINK pSink)
    678706{
    679707    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
    680     PDMAUDIOSTREAMCMD const enmCmd = fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE;
    681 
     708    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    682709    int rc = RTCritSectEnter(&pSink->CritSect);
    683710    AssertRCReturn(rc, rc);
     711    char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
     712    LogFunc(("Starting '%s'. Old status: %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
    684713
    685714    /*
    686      * Input sink and no recording source set? Return early without updating the stream status...
     715     * Make sure the sink and its streams are all stopped.
    687716     */
    688     if (   pSink->enmDir == PDMAUDIODIR_IN
    689         && pSink->In.pStreamRecSource == NULL)
    690     {
    691         /** @todo r=bird: will the cause trouble for the stream status management? */
    692     }
     717    if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
     718        Assert(pSink->fStatus == AUDMIXSINK_STS_NONE);
    693719    else
    694720    {
     721        LogFunc(("%s: This sink is still running!! Stop it before starting it again.\n", pSink->pszName));
     722
     723        PAUDMIXSTREAM pStream;
     724        RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
     725        {
     726            /** @todo PDMAUDIOSTREAMCMD_STOP_NOW   */
     727            audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_DISABLE);
     728        }
     729        audioMixerSinkReset(pSink);
     730    }
     731
     732    /*
     733     * Send the command to the streams.
     734     */
     735    if (pSink->enmDir == PDMAUDIODIR_OUT)
     736    {
     737        PAUDMIXSTREAM pStream;
     738        RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
     739        {
     740            audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE);
     741        }
     742    }
     743    else
     744    {
     745        AssertReturnStmt(pSink->enmDir == PDMAUDIODIR_IN, RTCritSectLeave(&pSink->CritSect), VERR_INTERNAL_ERROR_3);
     746        if (pSink->In.pStreamRecSource) /* Any recording source set? */
     747        {
     748            Assert(audioMixerSinkIsStreamInList(pSink, pSink->In.pStreamRecSource));
     749            audioMixerStreamCtlInternal(pSink->In.pStreamRecSource, PDMAUDIOSTREAMCMD_ENABLE);
     750        }
     751    }
     752
     753    /*
     754     * Update the sink status.
     755     */
     756    pSink->fStatus = AUDMIXSINK_STS_RUNNING;
     757
     758    LogRel2(("Audio Mixer: Started sink '%s': %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
     759
     760    RTCritSectLeave(&pSink->CritSect);
     761    return VINF_SUCCESS;
     762}
     763
     764
     765/**
     766 * Helper for AudioMixerSinkDrainAndStop that calculates the max length a drain
     767 * operation should take.
     768 *
     769 * @returns The drain deadline (relative to RTTimeNanoTS).
     770 * @param   pSink               The sink.
     771 * @param   cbDmaLeftToDrain    The number of bytes in the DMA buffer left to
     772 *                              transfer into the mixbuf.
     773 */
     774static uint64_t audioMixerSinkDrainDeadline(PAUDMIXSINK pSink, uint32_t cbDmaLeftToDrain)
     775{
     776    /*
     777     * Calculate the max backend buffer size in mixbuf frames.
     778     * (This is somewhat similar to audioMixerSinkUpdateOutputCalcFramesToRead.)
     779     */
     780    uint32_t      cFramesStreamMax = 0;
     781    PAUDMIXSTREAM pMixStream;
     782    RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
     783    {
     784        /*LogFunc(("Stream '%s': %#x (%u frames)\n", pMixStream->pszName, pMixStream->fStatus, pMixStream->cFramesBackendBuffer));*/
     785        if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
     786        {
     787            uint32_t cFrames = pMixStream->cFramesBackendBuffer;
     788            if (PDMAudioPropsHz(&pMixStream->pStream->Props) == PDMAudioPropsHz(&pSink->MixBuf.Props))
     789            { /* likely */ }
     790            else
     791                cFrames = cFrames * PDMAudioPropsHz(&pSink->MixBuf.Props) / PDMAudioPropsHz(&pMixStream->pStream->Props);
     792            if (cFrames > cFramesStreamMax)
     793            {
     794                Log4Func(("%s: cFramesStreamMax %u -> %u; %s\n", pSink->pszName, cFramesStreamMax, cFrames, pMixStream->pszName));
     795                cFramesStreamMax = cFrames;
     796            }
     797        }
     798    }
     799
     800    /*
     801     * Combine that with the pending DMA and mixbuf content, then convert
     802     * to nanoseconds and apply a fudge factor to get a generous deadline.
     803     */
     804    uint32_t const cFramesDmaAndMixBuf = PDMAudioPropsBytesToFrames(&pSink->MixBuf.Props, cbDmaLeftToDrain)
     805                                       + AudioMixBufLive(&pSink->MixBuf);
     806    uint64_t const cNsToDrainMax       = PDMAudioPropsFramesToNano(&pSink->MixBuf.Props, cFramesDmaAndMixBuf + cFramesStreamMax);
     807    uint64_t const nsDeadline          = cNsToDrainMax * 2;
     808    LogFlowFunc(("%s: cFramesStreamMax=%#x cFramesDmaAndMixBuf=%#x -> cNsToDrainMax=%RU64 -> %RU64\n",
     809                 pSink->pszName, cFramesStreamMax, cFramesDmaAndMixBuf, cNsToDrainMax, nsDeadline));
     810    return nsDeadline;
     811}
     812
     813
     814/**
     815 * Kicks off the draining and stopping playback/capture on the mixer sink.
     816 *
     817 * For input streams this causes an immediate stop, as draining only makes sense
     818 * to output stream in the VBox device context.
     819 *
     820 * @returns VBox status code.  Generally always VINF_SUCCESS unless the input
     821 *          is invalid.  Individual driver errors are suppressed and ignored.
     822 * @param   pSink       Mixer sink to control.
     823 * @param   cbComming   The number of bytes still left in the device's DMA
     824 *                      buffers that the update job has yet to transfer.  This
     825 *                      is ignored for input streams.
     826 */
     827int AudioMixerSinkDrainAndStop(PAUDMIXSINK pSink, uint32_t cbComming)
     828{
     829    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     830    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
     831
     832    int rc = RTCritSectEnter(&pSink->CritSect);
     833    AssertRCReturn(rc, rc);
     834    char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
     835    LogFunc(("Draining '%s' with %#x bytes left. Old status: %s\n",
     836             pSink->pszName, cbComming, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus) ));
     837
     838    if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
     839    {
    695840        /*
    696          * Send the command to the streams.
     841         * Output streams will be drained then stopped (all by the AIO thread).
     842         *
     843         * For streams we define that they shouldn't not be written to after we start draining,
     844         * so we have to hold back sending the command to them till we've processed all the
     845         * cbComming remaining bytes in the DMA buffer.
    697846         */
    698         if (pSink->enmDir == PDMAUDIODIR_IN)
    699         {
    700             /** @todo r=bird: we already check this. duh.   */
     847        if (pSink->enmDir == PDMAUDIODIR_OUT)
     848        {
     849            if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
     850            {
     851                Assert(!(pSink->fStatus & (AUDMIXSINK_STS_DRAINED_DMA | AUDMIXSINK_STS_DRAINED_MIXBUF)));
     852
     853                /* Update the status and draining member. */
     854                pSink->cbDmaLeftToDrain = cbComming;
     855                pSink->nsDrainDeadline  = audioMixerSinkDrainDeadline(pSink, cbComming);
     856                if (pSink->nsDrainDeadline > 0)
     857                {
     858                    pSink->nsDrainStarted   = RTTimeNanoTS();
     859                    pSink->nsDrainDeadline += pSink->nsDrainStarted;
     860                    pSink->fStatus         |= AUDMIXSINK_STS_DRAINING;
     861
     862                    /* Kick the AIO thread so it can keep pushing data till we're out of this
     863                       status. (The device's DMA timer won't kick it any more, so we must.) */
     864                    AudioMixerSinkSignalUpdateJob(pSink);
     865                }
     866                else
     867                {
     868                    LogFunc(("%s: No active streams, doing an immediate stop.\n"));
     869                    PAUDMIXSTREAM pStream;
     870                    RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
     871                    {
     872                        audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_DISABLE);
     873                    }
     874                    audioMixerSinkReset(pSink);
     875                }
     876            }
     877            else
     878                AssertMsgFailed(("Already draining '%s': %s\n",
     879                                 pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
     880        }
     881        /*
     882         * Input sinks are stopped immediately.
     883         *
     884         * It's the guest giving order here and we can't force it to accept data that's
     885         * already in the buffer pipeline or anything.  So, there can be no draining here.
     886         */
     887        else
     888        {
     889            AssertReturnStmt(pSink->enmDir == PDMAUDIODIR_IN, RTCritSectLeave(&pSink->CritSect), VERR_INTERNAL_ERROR_3);
    701890            if (pSink->In.pStreamRecSource) /* Any recording source set? */
    702891            {
    703                 PAUDMIXSTREAM pStream;
    704                 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
    705                 {
    706                     if (pStream == pSink->In.pStreamRecSource)
    707                     {
    708                         audioMixerStreamCtlInternal(pStream, enmCmd);
    709                         break;
    710                     }
    711                 }
     892                Assert(audioMixerSinkIsStreamInList(pSink, pSink->In.pStreamRecSource));
     893                audioMixerStreamCtlInternal(pSink->In.pStreamRecSource, PDMAUDIOSTREAMCMD_DISABLE);
    712894            }
    713         }
    714         else if (pSink->enmDir == PDMAUDIODIR_OUT)
    715         {
    716             PAUDMIXSTREAM pStream;
    717             RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
    718             {
    719                 audioMixerStreamCtlInternal(pStream, enmCmd);
    720             }
    721         }
    722         else
    723             AssertFailed();
    724 
    725         /*
    726          * Update the sink status.
    727          */
    728         if (fEnable)
    729         {
    730             /* Make sure to clear any other former flags again by assigning AUDMIXSINK_STS_RUNNING directly. */
    731             pSink->fStatus = AUDMIXSINK_STS_RUNNING;
    732         }
    733         else
    734         {
    735             if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
    736             {
    737                 /* Set the sink in a pending disable state first.
    738                  * The final status (disabled) will be set in the sink's iteration. */
    739                 pSink->fStatus |= AUDMIXSINK_STS_PENDING_DISABLE;
    740 
    741                 /* Kick the AIO thread so it can keep pushing data till we're out of this status. */
    742                 if (pSink->AIO.hEvent != NIL_RTSEMEVENT)
    743                     AudioMixerSinkSignalUpdateJob(pSink);
    744             }
    745         }
    746     }
    747 
    748     char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
    749     LogRel2(("Audio Mixer: Set new status of sink '%s': %s (enmCmd=%s)\n",
    750              pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus), PDMAudioStrmCmdGetName(enmCmd)));
    751 
     895            audioMixerSinkReset(pSink);
     896        }
     897    }
     898    else
     899        LogFunc(("%s: Not running\n", pSink->pszName));
     900
     901    LogRel2(("Audio Mixer: Started draining sink '%s': %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
    752902    RTCritSectLeave(&pSink->CritSect);
    753903    return VINF_SUCCESS;
    754904}
     905
     906
    755907
    756908/**
     
    773925    if (RT_SUCCESS(rc))
    774926    {
     927        pSink->uMagic   = AUDMIXSINK_MAGIC;
    775928        pSink->pParent  = pMixer;
    776929        pSink->enmDir   = enmDir;
     
    823976        PAUDIOMIXER pMixer = pSink->pParent;
    824977        AssertPtr(pMixer);
     978        Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
    825979
    826980        audioMixerRemoveSinkInternal(pMixer, pSink);
     
    8481002    LogFunc(("%s\n", pSink->pszName));
    8491003
     1004    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
     1005    pSink->uMagic = AUDMIXSINK_MAGIC_DEAD;
     1006
    8501007    PAUDMIXSTREAM pStream, pStreamNext;
    8511008    RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
    8521009    {
    8531010        audioMixerSinkRemoveStreamInternal(pSink, pStream);
    854         audioMixerStreamDestroyInternal(pStream, pDevIns);
     1011        audioMixerStreamDestroyInternal(pStream, pDevIns); /* (Unlike the other two, this frees the stream structure.) */
    8551012    }
    8561013
     
    9041061{
    9051062    AssertPtrReturn(pSink, 0);
    906 
     1063    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    9071064    AssertMsg(pSink->enmDir == PDMAUDIODIR_IN, ("%s: Can't read from a non-input sink\n", pSink->pszName));
    9081065
    9091066    int rc = RTCritSectEnter(&pSink->CritSect);
    910     if (RT_FAILURE(rc))
    911         return 0;
     1067    AssertRCReturn(rc, 0);
    9121068
    9131069    uint32_t cbReadable = 0;
     
    9471103PAUDMIXSTREAM AudioMixerSinkGetRecordingSource(PAUDMIXSINK pSink)
    9481104{
     1105    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    9491106    int rc = RTCritSectEnter(&pSink->CritSect);
    950     if (RT_FAILURE(rc))
    951         return NULL;
     1107    AssertRCReturn(rc, NULL);
    9521108
    9531109    AssertMsg(pSink->enmDir == PDMAUDIODIR_IN, ("Specified sink is not an input sink\n"));
     
    9711127{
    9721128    AssertPtrReturn(pSink, 0);
    973 
     1129    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    9741130    AssertMsg(pSink->enmDir == PDMAUDIODIR_OUT, ("%s: Can't write to a non-output sink\n", pSink->pszName));
    9751131
    9761132    int rc = RTCritSectEnter(&pSink->CritSect);
    977     if (RT_FAILURE(rc))
    978         return 0;
     1133    AssertRCReturn(rc, 0);
    9791134
    9801135    uint32_t cbWritable = 0;
    9811136
    9821137    if (    (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
    983         && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
     1138        && !(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
    9841139    {
    9851140        cbWritable = AudioMixBufFreeBytes(&pSink->MixBuf);
     
    10041159{
    10051160    AssertPtrReturn(pSink, PDMAUDIODIR_INVALID);
     1161    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    10061162
    10071163    /** @todo the sink direction should be static...    */
     
    10271183    if (!pSink)
    10281184        return AUDMIXSINK_STS_NONE;
     1185    AssertPtr(pSink);
     1186    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    10291187
    10301188    int rc2 = RTCritSectEnter(&pSink->CritSect);
    1031     if (RT_FAILURE(rc2))
    1032         return AUDMIXSINK_STS_NONE;
     1189    AssertRCReturn(rc2, AUDMIXSINK_STS_NONE);
    10331190
    10341191    /* If the dirty flag is set, there is unprocessed data in the sink. */
     
    10531210    if (!pSink)
    10541211        return false;
     1212    AssertPtr(pSink);
     1213    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    10551214
    10561215    int rc2 = RTCritSectEnter(&pSink->CritSect);
    1057     if (RT_FAILURE(rc2))
    1058         return false;
     1216    AssertRCReturn(rc2, false);
    10591217
    10601218    const bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
     
    10811239{
    10821240    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     1241    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    10831242    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    10841243    AssertReturn(cbBuf,    VERR_INVALID_PARAMETER);
     
    12161375void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
    12171376{
     1377    AssertPtr(pSink);
     1378    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
     1379    if (pStream)
     1380        Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC);
     1381
    12181382    int rc2 = RTCritSectEnter(&pSink->CritSect);
    12191383    AssertRC(rc2);
     
    12481412
    12491413/**
    1250  * Resets the sink's state.
    1251  *
    1252  * @param   pSink               Sink to reset.
    1253  */
    1254 static void audioMixerSinkReset(PAUDMIXSINK pSink)
     1414 * Removes all attached streams from a given sink.
     1415 *
     1416 * @param pSink                 Sink to remove attached streams from.
     1417 */
     1418void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
    12551419{
    12561420    if (!pSink)
    12571421        return;
    12581422
     1423    int rc2 = RTCritSectEnter(&pSink->CritSect);
     1424    AssertRC(rc2);
     1425
     1426    audioMixerSinkRemoveAllStreamsInternal(pSink);
     1427
     1428    pSink->cStreams = 0;
     1429
     1430    rc2 = RTCritSectLeave(&pSink->CritSect);
     1431    AssertRC(rc2);
     1432}
     1433
     1434/**
     1435 * Resets the sink's state.
     1436 *
     1437 * @param   pSink               Sink to reset.
     1438 */
     1439static void audioMixerSinkReset(PAUDMIXSINK pSink)
     1440{
     1441    if (!pSink)
     1442        return;
     1443
    12591444    LogFunc(("[%s]\n", pSink->pszName));
    12601445
     
    12661451    /* Reset status. */
    12671452    pSink->fStatus = AUDMIXSINK_STS_NONE;
    1268 }
    1269 
    1270 /**
    1271  * Removes all attached streams from a given sink.
    1272  *
    1273  * @param pSink                 Sink to remove attached streams from.
    1274  */
    1275 void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
    1276 {
    1277     if (!pSink)
    1278         return;
    1279 
    1280     int rc2 = RTCritSectEnter(&pSink->CritSect);
    1281     AssertRC(rc2);
    1282 
    1283     audioMixerSinkRemoveAllStreamsInternal(pSink);
    1284 
    1285     pSink->cStreams = 0;
    1286 
    1287     rc2 = RTCritSectLeave(&pSink->CritSect);
    1288     AssertRC(rc2);
    12891453}
    12901454
     
    13201484{
    13211485    AssertPtrReturn(pSink,     VERR_INVALID_POINTER);
     1486    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    13221487    AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
    13231488    AssertReturn(AudioHlpPcmPropsAreValid(pPCMProps), VERR_INVALID_PARAMETER);
     
    14671632{
    14681633    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     1634    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
     1635    if (pStream)
     1636        Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC);
    14691637
    14701638    int rc = RTCritSectEnter(&pSink->CritSect);
    1471     if (RT_FAILURE(rc))
    1472         return rc;
     1639    AssertRCReturn(rc, rc);
    14731640
    14741641    rc = audioMixerSinkSetRecSourceInternal(pSink, pStream);
     
    14901657{
    14911658    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     1659    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    14921660    AssertPtrReturn(pVol,  VERR_INVALID_POINTER);
    14931661
    14941662    int rc = RTCritSectEnter(&pSink->CritSect);
    1495     if (RT_FAILURE(rc))
    1496         return rc;
     1663    AssertRCReturn(rc, rc);
    14971664
    14981665    memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
     
    15791746    pSink->tsLastUpdatedMs = RTTimeMilliTS();
    15801747
    1581     /*
    1582      * Deal with pending disable.  The general case is that we reset
    1583      * the sink when all streams have been disabled, however input is
    1584      * currently a special case where we only care about the one
    1585      * recording source...
    1586      */
    1587     if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE)
    1588     {
    1589 #if 1
    1590         uint32_t const  cStreams         = 1;
    1591         uint32_t        cStreamsDisabled = 1;
    1592         pMixStream = pSink->In.pStreamRecSource;
    1593 #else
    1594         uint32_t const  cStreams         = pSink->cStreams;
    1595         uint32_t        cStreamsDisabled = pSink->cStreams;
    1596         RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
    1597 #endif
    1598         {
    1599             if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
    1600             {
    1601                 PDMAUDIOSTREAMSTATE const enmState = pMixStream->pConn->pfnStreamGetState(pMixStream->pConn, pMixStream->pStream);
    1602                 if (enmState >= PDMAUDIOSTREAMSTATE_ENABLED)
    1603                     cStreamsDisabled--;
    1604             }
    1605         }
    1606         Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams));
    1607         if (cStreamsDisabled == cStreams)
    1608             audioMixerSinkReset(pSink);
    1609     }
    1610 
     1748    Assert(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING));
    16111749    return rc;
    16121750}
     
    16741812{
    16751813    PAUDMIXSTREAM pMixStream;
     1814    Assert(!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_MIXBUF) || AudioMixBufUsed(&pSink->MixBuf) == 0);
    16761815
    16771816    /*
     
    18141953         * Update the dirty flag for what it's worth.
    18151954         */
    1816         if (AudioMixBufUsed(&pSink->MixBuf))
     1955        if (AudioMixBufUsed(&pSink->MixBuf) > 0)
    18171956            pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
    18181957        else
     
    18381977
    18391978    /* Update last updated timestamp. */
    1840     pSink->tsLastUpdatedMs = RTTimeMilliTS();
     1979    uint64_t const nsNow   = RTTimeNanoTS();
     1980    pSink->tsLastUpdatedMs = nsNow / RT_NS_1MS;
    18411981
    18421982    /*
     
    18441984     * We reset the sink when all streams have been disabled.
    18451985     */
    1846     if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE)
    1847     {
     1986    if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
     1987    { /* likely, till we get to the end */ }
     1988    else if (nsNow <= pSink->nsDrainDeadline)
     1989    {
     1990        /* Have we drained the mixbuf now?  If so, update status and send drain
     1991           command to streams.  (As mentioned elsewhere we don't want to confuse
     1992           driver code by sending drain command while there is still data to write.) */
     1993        Assert((pSink->fStatus & AUDMIXSINK_STS_DIRTY) == (AudioMixBufUsed(&pSink->MixBuf) > 0 ? AUDMIXSINK_STS_DIRTY : 0));
     1994        if ((pSink->fStatus & (AUDMIXSINK_STS_DRAINED_MIXBUF | AUDMIXSINK_STS_DIRTY)) == 0)
     1995        {
     1996            LogFunc(("Sink '%s': Setting AUDMIXSINK_STS_DRAINED_MIXBUF and sending drain command to streams (after %RU64 ns).\n",
     1997                     pSink->pszName, nsNow - pSink->nsDrainStarted));
     1998            pSink->fStatus |= AUDMIXSINK_STS_DRAINED_MIXBUF;
     1999
     2000            RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
     2001            {
     2002                pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, PDMAUDIOSTREAMCMD_DRAIN);
     2003            }
     2004        }
     2005
     2006        /* Check if all streams has stopped, and if so we stop the sink. */
    18482007        uint32_t const  cStreams         = pSink->cStreams;
    18492008        uint32_t        cStreamsDisabled = pSink->cStreams;
     
    18572016            }
    18582017        }
    1859         Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams));
    1860         if (cStreamsDisabled == cStreams)
    1861             audioMixerSinkReset(pSink);
     2018
     2019        if (cStreamsDisabled != cStreams)
     2020            Log3Func(("Sink '%s': %u out of %u streams disabled (after %RU64 ns).\n",
     2021                      pSink->pszName, cStreamsDisabled, cStreams, nsNow - pSink->nsDrainStarted));
     2022        else
     2023        {
     2024            LogFunc(("Sink '%s': All %u streams disabled. Drain done after %RU64 ns.\n",
     2025                     pSink->pszName, cStreamsDisabled, cStreams, nsNow - pSink->nsDrainStarted));
     2026            audioMixerSinkReset(pSink); /* clears the status */
     2027        }
     2028    }
     2029    else
     2030    {
     2031        /* Draining timed out. Just do an instant stop. */
     2032        LogFunc(("Sink '%s': pending disable timed out after %RU64 ns!\n", pSink->pszName, nsNow - pSink->nsDrainStarted));
     2033        RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
     2034        {
     2035            pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, PDMAUDIOSTREAMCMD_DISABLE);
     2036        }
     2037        audioMixerSinkReset(pSink); /* clears the status */
    18622038    }
    18632039
     
    18742050{
    18752051    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     2052    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    18762053    int rc = RTCritSectEnter(&pSink->CritSect);
    18772054    AssertRCReturn(rc, rc);
     
    19082085    PAUDMIXSINK pSink = (PAUDMIXSINK)pvUser;
    19092086    AssertPtr(pSink);
     2087    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    19102088    RT_NOREF(hThreadSelf);
    19112089
     
    19192097
    19202098        RTCritSectEnter(&pSink->CritSect);
    1921         if (pSink->fStatus & (AUDMIXSINK_STS_RUNNING | AUDMIXSINK_STS_PENDING_DISABLE))
     2099        if (pSink->fStatus & (AUDMIXSINK_STS_RUNNING | AUDMIXSINK_STS_DRAINING))
    19222100        {
    19232101            /*
     
    19452123             * DMA timer as it has normally stopped running at this point.
    19462124             */
    1947             if (!(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
     2125            if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
    19482126            { /* likely */ }
    19492127            else
     
    19892167{
    19902168    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     2169    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    19912170    int rc = RTCritSectEnter(&pSink->CritSect);
    19922171    AssertRCReturn(rc, rc);
     
    20682247{
    20692248    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     2249    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    20702250    int rc = RTCritSectEnter(&pSink->CritSect);
    20712251    AssertRCReturn(rc, rc);
     
    21192299     */
    21202300    AssertReturn(pSink, offStream);
     2301    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    21212302    AssertReturn(pCircBuf, offStream);
    21222303    Assert(RTCritSectIsOwner(&pSink->CritSect));
     2304    RT_NOREF(idStream);
    21232305
    21242306    /*
     
    21292311    uint32_t       cbToTransfer       = RT_MIN(cbCircBufReadable, cbSinkWritable);
    21302312    /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
    2131     cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer);
    2132 
    2133     Log3Func(("idStream=%#x: cbSinkWritable=%#RX32 cbCircBufReadable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",
     2313    uint32_t const cbToTransfer2      = cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer);
     2314
     2315    Log3Func(("idStream=%u: cbSinkWritable=%#RX32 cbCircBufReadable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",
    21342316              idStream, cbSinkWritable, cbCircBufReadable, cbToTransfer, offStream));
    2135     RT_NOREF(idStream);
     2317    AssertMsg(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING) || cbCircBufReadable == pSink->cbDmaLeftToDrain,
     2318              ("cbCircBufReadable=%#x cbDmaLeftToDrain=%#x\n", cbCircBufReadable, pSink->cbDmaLeftToDrain));
    21362319
    21372320    /*
     
    21492332        Assert(cbWritten <= cbSrcBuf);
    21502333
    2151         Log2Func(("idStream=%#x: %#RX32/%#zx bytes read @%#RX64\n", idStream, cbWritten, cbSrcBuf, offStream));
     2334        Log2Func(("idStream=%u: %#RX32/%#zx bytes read @%#RX64\n", idStream, cbWritten, cbSrcBuf, offStream));
    21522335#ifdef VBOX_WITH_DTRACE
    21532336        VBOXDD_AUDIO_MIXER_SINK_AIO_OUT(idStream, cbWritten, offStream);
     
    21662349        cbToTransfer -= cbWritten;
    21672350    }
     2351
     2352    /*
     2353     * Advance drain status.
     2354     */
     2355    if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
     2356    { /* likely for most of the playback time ... */ }
     2357    else if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_DMA))
     2358    {
     2359        if (cbToTransfer2 >= pSink->cbDmaLeftToDrain)
     2360        {
     2361            Assert(cbToTransfer2 == pSink->cbDmaLeftToDrain);
     2362            Log3Func(("idStream=%u/'%s': Setting AUDMIXSINK_STS_DRAINED_DMA.\n", idStream, pSink->pszName));
     2363            pSink->cbDmaLeftToDrain = 0;
     2364            pSink->fStatus         |= AUDMIXSINK_STS_DRAINED_DMA;
     2365        }
     2366        else
     2367        {
     2368            pSink->cbDmaLeftToDrain -= cbToTransfer2;
     2369            Log3Func(("idStream=%u/'%s': still %#x bytes left in the DMA buffer\n",
     2370                      idStream, pSink->pszName, pSink->cbDmaLeftToDrain));
     2371        }
     2372    }
     2373    else
     2374        Assert(cbToTransfer2 == 0);
    21682375
    21692376    return offStream;
     
    21912398     */
    21922399    AssertReturn(pSink, offStream);
     2400    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    21932401    AssertReturn(pCircBuf, offStream);
    21942402    Assert(RTCritSectIsOwner(&pSink->CritSect));
     
    22042412    cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer);
    22052413
    2206     Log3Func(("idStream=%#x: cbSinkReadable=%#RX32 cbCircBufWritable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",
     2414    Log3Func(("idStream=%u: cbSinkReadable=%#RX32 cbCircBufWritable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",
    22072415              idStream, cbSinkReadable, cbCircBufWritable, cbToTransfer, offStream));
    22082416    RT_NOREF(idStream);
     
    22702478{
    22712479    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     2480    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    22722481    return RTSemEventSignal(pSink->AIO.hEvent);
    22732482}
     
    22832492{
    22842493    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     2494    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    22852495    return RTCritSectEnter(&pSink->CritSect);
    22862496}
     
    22962506{
    22972507    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     2508    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    22982509    return RTCritSectTryEnter(&pSink->CritSect);
    22992510}
     
    23202531 * @param   pVolMaster          Master volume to set.
    23212532 */
    2322 static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
     2533static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, PCPDMAUDIOVOLUME pVolMaster)
    23232534{
    23242535    AssertPtrReturn(pSink,      VERR_INVALID_POINTER);
     2536    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    23252537    AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
    23262538
     
    23732585{
    23742586    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     2587    Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
    23752588    RT_NOREF(enmOp);
    23762589    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     
    24312644static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd)
    24322645{
     2646    Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
    24332647    AssertPtrReturn(pMixStream->pConn, VERR_AUDIO_STREAM_NOT_READY);
    24342648    AssertPtrReturn(pMixStream->pStream, VERR_AUDIO_STREAM_NOT_READY);
     
    24522666static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream)
    24532667{
     2668    Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
     2669
    24542670    /*
    24552671     * Reset the mixer status to start with.
     
    25212737
    25222738/**
    2523  * Destroys a mixer stream, internal version.
     2739 * Destroys & frees a mixer stream, internal version.
    25242740 *
    25252741 * Worker for audioMixerSinkDestroyInternal and AudioMixerStreamDestroy.
     
    25332749
    25342750    LogFunc(("%s\n", pMixStream->pszName));
     2751    Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
    25352752
    25362753    if (pMixStream->pConn) /* Stream has a connector interface present? */
     
    25752792        return;
    25762793
     2794/** @todo r=bird: Wrng critsect for audioMixerSinkRemoveStreamInternal   */
    25772795    int rc2 = RTCritSectEnter(&pMixStream->CritSect);
    25782796    AssertRC(rc2);
  • trunk/src/VBox/Devices/Audio/AudioMixer.h

    r88957 r88991  
    4242typedef struct AUDIOMIXER
    4343{
     44    /** Magic value (AUDIOMIXER_MAGIC). */
     45    uintptr_t               uMagic;
    4446    /** The mixer's name. */
    4547    char                   *pszName;
    46     /** The mixer's critical section. */
    47     RTCRITSECT              CritSect;
    4848    /** The master volume of this mixer. */
    4949    PDMAUDIOVOLUME          VolMaster;
     
    5454    /** Mixer flags. See AUDMIXER_FLAGS_XXX. */
    5555    uint32_t                fFlags;
     56    /** The mixer's critical section. */
     57    RTCRITSECT              CritSect;
    5658} AUDIOMIXER;
    5759/** Pointer to an audio mixer instance. */
    5860typedef AUDIOMIXER *PAUDIOMIXER;
     61
     62/** Value for AUDIOMIXER::uMagic. (Attilio Joseph "Teo" Macero)  */
     63#define AUDIOMIXER_MAGIC                UINT32_C(0x19251030)
     64/** Value for AUDIOMIXER::uMagic after destruction. */
     65#define AUDIOMIXER_MAGIC_DEAD           UINT32_C(0x20080219)
    5966
    6067/** @name AUDMIXER_FLAGS_XXX - For AudioMixerCreate().
     
    7784    /** List entry on AUDMIXSINK::lstStreams. */
    7885    RTLISTNODE              Node;
    79     /** Name of this stream. */
    80     char                   *pszName;
    81     /** The statistics prefix. */
    82     char                   *pszStatPrefix;
    83     /** Sink this stream is attached to. */
    84     PAUDMIXSINK             pSink;
     86    /** Magic value (AUDMIXSTREAM_MAGIC). */
     87    uint32_t                uMagic;
     88    /** The backend buffer size in frames (for draining deadline calc). */
     89    uint32_t                cFramesBackendBuffer;
    8590    /** Stream status of type AUDMIXSTREAM_STATUS_. */
    8691    uint32_t                fStatus;
     
    9196     * from the mixer buffer and to the drivers. */
    9297    bool                    fUnreliable;
     98    /** Name of this stream. */
     99    char                   *pszName;
     100    /** The statistics prefix. */
     101    char                   *pszStatPrefix;
     102    /** Sink this stream is attached to. */
     103    PAUDMIXSINK             pSink;
    93104    /** Pointer to audio connector being used. */
    94105    PPDMIAUDIOCONNECTOR     pConn;
     
    104115/** Pointer to an audio mixer stream. */
    105116typedef AUDMIXSTREAM *PAUDMIXSTREAM;
     117
     118/** Value for AUDMIXSTREAM::uMagic. (Jan Erik Kongshaug)  */
     119#define AUDMIXSTREAM_MAGIC                UINT32_C(0x19440704)
     120/** Value for AUDMIXSTREAM::uMagic after destruction. */
     121#define AUDMIXSTREAM_MAGIC_DEAD           UINT32_C(0x20191105)
     122
    106123
    107124/** @name AUDMIXSTREAM_STATUS_XXX - mixer stream status.
     
    133150    /** List entry on AUDIOMIXER::lstSinks. */
    134151    RTLISTNODE              Node;
     152    /** Magic value (AUDMIXSINK_MAGIC). */
     153    uint32_t                uMagic;
     154    /** The sink direction (either PDMAUDIODIR_IN or PDMAUDIODIR_OUT). */
     155    PDMAUDIODIR             enmDir;
    135156    /** Pointer to mixer object this sink is bound to. */
    136157    PAUDIOMIXER             pParent;
    137158    /** Name of this sink. */
    138159    char                   *pszName;
    139     /** The sink direction (either PDMAUDIODIR_IN or PDMAUDIODIR_OUT). */
    140     PDMAUDIODIR             enmDir;
    141160    /** The sink's PCM format (i.e. the guest device side). */
    142161    PDMAUDIOPCMPROPS        PCMProps;
    143162    /** Sink status bits - AUDMIXSINK_STS_XXX. */
    144163    uint32_t                fStatus;
     164    /** Number of bytes to be transferred from the device DMA buffer before the
     165     *  streams will be put into draining mode. */
     166    uint32_t                cbDmaLeftToDrain;
     167    /** The deadline for draining if it's pending. */
     168    uint64_t                nsDrainDeadline;
     169    /** When the draining startet (for logging). */
     170    uint64_t                nsDrainStarted;
    145171    /** Number of streams assigned. */
    146172    uint8_t                 cStreams;
     
    215241} AUDMIXSINK;
    216242
     243/** Value for AUDMIXSINK::uMagic. (Sir George Martin)  */
     244#define AUDMIXSINK_MAGIC                UINT32_C(0x19260103)
     245/** Value for AUDMIXSINK::uMagic after destruction. */
     246#define AUDMIXSINK_MAGIC_DEAD           UINT32_C(0x20160308)
     247
     248
    217249/** @name AUDMIXSINK_STS_XXX - Sink status bits.
    218250 * @{ */
     
    221253/** The sink is active and running. */
    222254#define AUDMIXSINK_STS_RUNNING               RT_BIT(0)
    223 /** The sink is in a pending disable state. */
    224 #define AUDMIXSINK_STS_PENDING_DISABLE       RT_BIT(1)
     255/** Draining the buffers and pending stop - output only. */
     256#define AUDMIXSINK_STS_DRAINING              RT_BIT(1)
     257/** Drained the DMA buffer. */
     258#define AUDMIXSINK_STS_DRAINED_DMA           RT_BIT(2)
     259/** Drained the mixer buffer, only waiting for streams (drivers) now. */
     260#define AUDMIXSINK_STS_DRAINED_MIXBUF        RT_BIT(3)
    225261/** Dirty flag.
    226262 * - For output sinks this means that there is data in the sink which has not
     
    228264 * - For input sinks this means that there is data in the sink which has been
    229265 *   recorded but not transferred to the destination yet. */
    230 #define AUDMIXSINK_STS_DIRTY                 RT_BIT(2)
     266#define AUDMIXSINK_STS_DIRTY                 RT_BIT(4)
    231267/** @} */
    232268
     
    258294int     AudioMixerSinkCreateStream(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOSTREAMCFG pCfg,
    259295                                   PPDMDEVINS pDevIns, PAUDMIXSTREAM *ppStream);
    260 int     AudioMixerSinkEnable(PAUDMIXSINK pSink, bool fEnable);
     296int     AudioMixerSinkStart(PAUDMIXSINK pSink);
     297int     AudioMixerSinkDrainAndStop(PAUDMIXSINK pSink, uint32_t cbComming);
    261298void AudioMixerSinkDestroy(PAUDMIXSINK pSink, PPDMDEVINS pDevIns);
    262299uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink);
  • trunk/src/VBox/Devices/Audio/DevHdaStream.cpp

    r88954 r88991  
    905905    if (pSink)
    906906    {
    907         if (fEnable && pStreamR3->State.pAioRegSink != pSink)
     907        if (fEnable)
    908908        {
    909             if (pStreamR3->State.pAioRegSink)
     909            if (pStreamR3->State.pAioRegSink != pSink)
    910910            {
    911                 rc = AudioMixerSinkRemoveUpdateJob(pStreamR3->State.pAioRegSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3);
    912                 AssertRC(rc);
     911                if (pStreamR3->State.pAioRegSink)
     912                {
     913                    rc = AudioMixerSinkRemoveUpdateJob(pStreamR3->State.pAioRegSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3);
     914                    AssertRC(rc);
     915                }
     916                rc = AudioMixerSinkAddUpdateJob(pSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3,
     917                                                pStreamShared->State.Cfg.Device.cMsSchedulingHint);
     918                AssertLogRelRC(rc);
     919                pStreamR3->State.pAioRegSink = RT_SUCCESS(rc) ? pSink : NULL;
    913920            }
    914             rc = AudioMixerSinkAddUpdateJob(pSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3,
    915                                             pStreamShared->State.Cfg.Device.cMsSchedulingHint);
    916             AssertLogRelRC(rc);
    917             pStreamR3->State.pAioRegSink = RT_SUCCESS(rc) ? pSink : NULL;
     921            rc = AudioMixerSinkStart(pSink);
    918922        }
    919         if (RT_SUCCESS(rc))
    920             rc = AudioMixerSinkEnable(pSink, fEnable);
     923        else
     924            rc = AudioMixerSinkDrainAndStop(pSink,
     925                                            pStreamR3->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf) : 0);
    921926    }
    922927    if (   RT_SUCCESS(rc)
  • trunk/src/VBox/Devices/Audio/DevIchAc97.cpp

    r88954 r88991  
    974974            }
    975975        }
     976
     977        if (RT_SUCCESS(rc))
     978            rc = AudioMixerSinkStart(pSink);
    976979    }
    977980    else
     981    {
    978982        rc = ichac97R3StreamClose(pStream);
    979 
    980     if (RT_SUCCESS(rc))
    981     {
    982         /* First, enable or disable the stream and the stream's sink, if any. */
    983         rc = AudioMixerSinkEnable(pSink, fEnable);
     983        if (RT_SUCCESS(rc))
     984            rc = AudioMixerSinkDrainAndStop(pSink,
     985                                            pStreamCC->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStreamCC->State.pCircBuf) : 0);
    984986    }
    985987
  • trunk/src/VBox/Devices/Audio/DevSB16.cpp

    r88955 r88991  
    20452045    }
    20462046
    2047     /* First, enable or disable the stream and the stream's sink. */
    2048     rc = AudioMixerSinkEnable(pSink, fEnable);
    2049     AssertRCReturn(rc, rc);
     2047    /* Tell the mixer. */
     2048    if (fEnable)
     2049    {
     2050        rc = AudioMixerSinkStart(pSink);
     2051        AssertRCReturn(rc, rc);
     2052    }
     2053    else
     2054    {
     2055        rc = AudioMixerSinkDrainAndStop(pSink, pStream->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStream->State.pCircBuf) : 0);
     2056        AssertRCReturn(rc, rc);
     2057    }
    20502058
    20512059    pStream->State.fEnabled = fEnable;
  • trunk/src/VBox/Devices/Audio/DrvAudio.cpp

    r88898 r88991  
    251251            /** The current pre-buffering read offset. */
    252252            uint32_t            offPreBuf;
    253             /** Number of bytes we've prebuffered. */
     253            /** Number of bytes we've pre-buffered. */
    254254            uint32_t            cbPreBuffered;
    255255            /** The pre-buffering threshold expressed in bytes. */
     
    363363    RTREQPOOL               hReqPool;
    364364
    365     /** Handle to the disable-iteration timer. */
    366     TMTIMERHANDLE           hTimer;
    367     /** Set if hTimer is armed. */
    368     bool volatile           fTimerArmed;
    369     /** Unique name for the the disable-iteration timer.  */
    370     char                    szTimerName[23];
    371 
    372365#ifdef VBOX_WITH_AUDIO_ENUM
    373366    /** Handle to the timer for delayed re-enumeration of backend devices. */
     
    547540    {
    548541        PDMHOSTAUDIOSTREAMSTATE enmState = pThis->pHostDrvAudio->pfnStreamGetState(pThis->pHostDrvAudio, pStreamEx->pBackend);
    549         Assert(enmState > PDMHOSTAUDIOSTREAMSTATE_INVALID && enmState < PDMHOSTAUDIOSTREAMSTATE_END);
    550542        Log9Func(("%s: %s\n", pStreamEx->Core.szName, PDMHostAudioStreamStateGetName(enmState) ));
     543        Assert(   enmState > PDMHOSTAUDIOSTREAMSTATE_INVALID
     544               && enmState < PDMHOSTAUDIOSTREAMSTATE_END
     545               && (enmState != PDMHOSTAUDIOSTREAMSTATE_DRAINING || pStreamEx->Guest.Cfg.enmDir == PDMAUDIODIR_OUT));
    551546        return enmState;
    552547    }
    553548    Log9Func(("%s: not-working\n", pStreamEx->Core.szName));
    554549    return PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
     550}
     551
     552
     553/**
     554 * Worker for drvAudioStreamProcessBackendStateChange that completes draining.
     555 */
     556DECLINLINE(void) drvAudioStreamProcessBackendStateChangeWasDraining(PDRVAUDIOSTREAM pStreamEx)
     557{
     558    Log(("drvAudioStreamProcessBackendStateChange: Stream '%s': Done draining - disabling stream.\n", pStreamEx->Core.szName));
     559    pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PENDING_DISABLE);
     560    drvAudioStreamResetInternal(pStreamEx);
    555561}
    556562
     
    583589
    584590        case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
    585             /* The stream has stopped working.  Switch to noplay mode. */
     591        case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
     592            /* The stream has stopped working or is inactive.  Switch stop any draining & to noplay mode. */
     593            if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)
     594                drvAudioStreamProcessBackendStateChangeWasDraining(pStreamEx);
    586595            if (enmDir == PDMAUDIODIR_OUT)
    587596                pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
     
    600609                       or not, resetting the stream state. */
    601610                    drvAudioStreamResetInternal(pStreamEx);
     611                    break;
     612
     613                case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
     614                    /* Complete the draining. May race the iterate code. */
     615                    if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)
     616                        drvAudioStreamProcessBackendStateChangeWasDraining(pStreamEx);
    602617                    break;
    603618
     
    611626            break;
    612627
    613         case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
    614             /* Stream is now inactive. Switch to noplay mode. */
    615             if (enmDir == PDMAUDIODIR_OUT)
    616                 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
     628        case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
     629            /* We do all we need to do when issuing the DRAIN command. */
     630            Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE);
    617631            break;
    618632
     
    24202434    {
    24212435        if (   (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY /* don't really need this check, do we? */)
    2422             && enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY)
     2436            && (   enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY
     2437                || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING) )
    24232438        {
    24242439            /** @todo Backend will change to explicit methods here, so please don't simplify
     
    25092524
    25102525/**
    2511  * @callback_method_impl{FNTMTIMERDRV}
    2512  */
    2513 static DECLCALLBACK(void) drvAudioEmergencyIterateTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
    2514 {
    2515     PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
    2516     RT_NOREF(hTimer, pvUser);
    2517     RTCritSectEnter(&pThis->CritSect);
    2518 
    2519     /*
    2520      * Iterate any stream with the pending-disable flag set.
    2521      */
    2522     uint32_t        cMilliesToNext = 0;
    2523     PDRVAUDIOSTREAM pStreamEx, pStreamExNext;
    2524     RTListForEachSafe(&pThis->lstStreams, pStreamEx, pStreamExNext, DRVAUDIOSTREAM, ListEntry)
    2525     {
    2526         if (   pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC
    2527             && pStreamEx->cRefs >= 1)
    2528         {
    2529             if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)
    2530             {
    2531                 drvAudioStreamIterateInternal(pThis, pStreamEx);
    2532 
    2533                 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)
    2534                     cMilliesToNext = 10;
    2535             }
    2536         }
    2537     }
    2538 
    2539     /*
    2540      * Re-arm the timer if we still got streams in the pending state.
    2541      */
    2542     if (cMilliesToNext)
    2543     {
    2544         pThis->fTimerArmed = true;
    2545         PDMDrvHlpTimerSetMillies(pDrvIns, pThis->hTimer, cMilliesToNext);
    2546     }
    2547     else
    2548         pThis->fTimerArmed = false;
    2549 
    2550     RTCritSectLeave(&pThis->CritSect);
    2551 }
    2552 
    2553 
    2554 /**
    25552526 * Controls an audio stream.
    25562527 *
     
    25782549            if (!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED))
    25792550            {
    2580                 /* Is a pending disable outstanding? Then disable first. */
     2551                /* Are we still draining this stream? Then we must disable it first. */
    25812552                if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)
     2553                {
     2554                    LogFunc(("Stream '%s' is still draining - disabling...\n", pStreamEx->Core.szName));
    25822555                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     2556                    AssertRC(rc);
     2557                    if (drvAudioStreamGetBackendState(pThis, pStreamEx) != PDMHOSTAUDIOSTREAMSTATE_DRAINING)
     2558                    {
     2559                        pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PENDING_DISABLE);
     2560                        drvAudioStreamResetInternal(pStreamEx);
     2561                        rc = VINF_SUCCESS;
     2562                    }
     2563                }
     2564
    25832565                if (RT_SUCCESS(rc))
    25842566                {
     
    25972579                                    pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF;
    25982580                                break;
     2581                            case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
     2582                                AssertFailed();
    25992583                            case PDMHOSTAUDIOSTREAMSTATE_OKAY:
    26002584                                pStreamEx->Out.enmPlayState = pStreamEx->Out.cbPreBufThreshold > 0
     
    26292613            if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED)
    26302614            {
    2631                 /*
    2632                  * For playback (output) streams first mark the host stream as pending disable,
    2633                  * so that the rest of the remaining audio data will be played first before
    2634                  * closing the stream.
    2635                  */
    2636                 if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT)
    2637                 {
    2638                     LogFunc(("[%s] Pending disable/pause\n", pStreamEx->Core.szName));
    2639                     pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE;
    2640                     PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
    2641 
    2642                     /* Schedule a follow up timer to the pending-disable state.  We cannot rely
    2643                        on the device to provide further callouts to finish the state transition.
    2644                        10ms is taking out of thin air and may be too course grained, we should
    2645                        really consider the amount of unplayed buffer in the backend and what not... */
    2646                     if (!pThis->fTimerArmed)
    2647                     {
    2648                         LogFlowFunc(("Arming emergency pending-disable hack...\n"));
    2649                         int rc2 = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hTimer, 10 /*ms*/);
    2650                         AssertRC(rc2);
    2651                         pThis->fTimerArmed = true;
    2652                     }
    2653                 }
    2654 
    2655                 /* Can we close the host stream as well (not in pending disable mode)? */
    2656                 if (!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE))
    2657                 {
    2658                     rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    2659                     if (RT_SUCCESS(rc))
    2660                         drvAudioStreamResetOnDisable(pStreamEx);
    2661                 }
     2615                rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     2616                LogFunc(("DISABLE '%s': Backend DISABLE -> %Rrc\n", pStreamEx->Core.szName, rc));
     2617                if (RT_SUCCESS(rc)) /** @todo ignore this and reset it anyway? */
     2618                    drvAudioStreamResetOnDisable(pStreamEx);
    26622619            }
    26632620            break;
     
    26882645            break;
    26892646
     2647        case PDMAUDIOSTREAMCMD_DRAIN:
     2648            /*
     2649             * Only for output streams and we don't want this command more than once.
     2650             */
     2651            AssertReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT, VERR_INVALID_FUNCTION);
     2652            AssertBreak(!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE));
     2653            if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED)
     2654            {
     2655                rc = VERR_INTERNAL_ERROR_2;
     2656                switch (pStreamEx->Out.enmPlayState)
     2657                {
     2658                    case DRVAUDIOPLAYSTATE_PREBUF:
     2659                        if (pStreamEx->Out.cbPreBuffered > 0)
     2660                        {
     2661                            LogFunc(("DRAIN '%s': Initiating draining of pre-buffered data...\n",
     2662                                     pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState)));
     2663                            pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_COMMITTING;
     2664                            pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE;
     2665                            PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
     2666                            break;
     2667                        }
     2668                        RT_FALL_THROUGH();
     2669                    case DRVAUDIOPLAYSTATE_NOPLAY:
     2670                    case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
     2671                    case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
     2672                        LogFunc(("DRAIN '%s': Nothing to drain (enmPlayState=%s)\n",
     2673                                 pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState)));
     2674                        rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     2675                        AssertRC(rc);
     2676                        drvAudioStreamResetOnDisable(pStreamEx);
     2677                        break;
     2678
     2679                    case DRVAUDIOPLAYSTATE_PLAY:
     2680                    case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
     2681                        LogFunc(("DRAIN '%s': Initiating backend draining (enmPlayState=%s -> NOPLAY) ...\n",
     2682                                 pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState)));
     2683                        pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
     2684                        rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DRAIN);
     2685                        if (RT_SUCCESS(rc))
     2686                        {
     2687                            pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE;
     2688                            PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
     2689                        }
     2690                        else
     2691                        {
     2692                            LogFunc(("DRAIN '%s': Backend DRAIN failed with %Rrc, disabling the stream instead...\n",
     2693                                     pStreamEx->Core.szName));
     2694                            rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     2695                            AssertRC(rc);
     2696                            drvAudioStreamResetOnDisable(pStreamEx);
     2697                        }
     2698                        break;
     2699
     2700                    case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
     2701                        LogFunc(("DRAIN '%s': Initiating draining of pre-buffered data (already committing)...\n",
     2702                                 pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState)));
     2703                        pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE;
     2704                        PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
     2705                        break;
     2706
     2707                    /* no default */
     2708                    case DRVAUDIOPLAYSTATE_INVALID:
     2709                    case DRVAUDIOPLAYSTATE_END:
     2710                        AssertFailedBreak();
     2711                }
     2712            }
     2713            break;
     2714
    26902715        default:
    26912716            rc = VERR_NOT_IMPLEMENTED;
     
    29923017
    29933018    /*
    2994      * Pending disable is really what we're here for.  This only happens to output streams.
    2995      */
    2996     int rc = VINF_SUCCESS;
     3019     * Pending disable is really what we're here for.
     3020     *
     3021     * This only happens to output streams.  We ASSUME the caller (MixerBuffer)
     3022     * implements a timeout on the draining, so we skip that here.
     3023     */
    29973024    if (!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE))
    29983025    { /* likely until we get to the end of the stream at least. */ }
     
    30003027    {
    30013028        AssertReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT, VINF_SUCCESS);
    3002         /** @todo Add a timeout to these proceedings.  A few times that of the reported
    3003          *        buffer size or something like that. */
    30043029
    30053030        /*
    3006          * Check if we have any data we need to write to the backend, try
    3007          * move it now.
     3031         * Move pre-buffered samples to the backend.
    30083032         */
    3009         /** @todo r=bird: It is possible the device has data buffered (e.g.
    3010          *        internal DMA buffer (HDA) or mixing buffer (HDA + AC'97).  We're
    3011          *        not taking that into account here.  I also suspect that neither is
    3012          *        the device/mixer code, and that if the guest is too quick disabling
    3013          *        the stream, it will just remain there till the next time something
    3014          *        is played.  That means that this code and associated timer hack
    3015          *        should probably not be here at all. */
    3016         uint32_t cFramesLive = 0;
    3017         switch (pStreamEx->Out.enmPlayState)
    3018         {
    3019             case DRVAUDIOPLAYSTATE_PLAY:                /* Nothing prebuffered. */
    3020             case DRVAUDIOPLAYSTATE_PLAY_PREBUF:         /* Not pre-buffering for current device. */
    3021             case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:      /* Output device isn't ready, drop it. */ /** @todo check state? */
    3022             case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:    /* No output device yet. */
    3023             case DRVAUDIOPLAYSTATE_NOPLAY:
    3024             case DRVAUDIOPLAYSTATE_INVALID:
    3025             case DRVAUDIOPLAYSTATE_END:
    3026                 /* no default, want warnings. */
    3027                 break;
    3028             case DRVAUDIOPLAYSTATE_PREBUF:
    3029             case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
    3030                 if (pStreamEx->Out.cbPreBuffered > 0)
     3033        if (pStreamEx->Out.enmPlayState == DRVAUDIOPLAYSTATE_PREBUF_COMMITTING)
     3034        {
     3035            if (pStreamEx->Out.cbPreBuffered > 0)
     3036            {
     3037                uint32_t cbIgnored = 0;
     3038                drvAudioStreamPreBufComitting(pThis, pStreamEx, NULL, 0, &cbIgnored);
     3039                Log3Func(("Stream '%s': Transferred %#x bytes\n", pStreamEx->Core.szName, cbIgnored));
     3040            }
     3041            if (pStreamEx->Out.cbPreBuffered == 0)
     3042            {
     3043                Log3Func(("Stream '%s': No more pre-buffered data -> NOPLAY + backend DRAIN\n", pStreamEx->Core.szName));
     3044                pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
     3045
     3046                int rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DRAIN);
     3047                if (RT_FAILURE(rc))
    30313048                {
    3032                     /* Must check the backend state here first and only try commit the
    3033                        pre-buffered samples if the backend is in working order. */
    3034                     PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
    3035                                                                      /* ^^^ (checks pThis->pHostDrvAudio != NULL too) */
    3036                     if (   (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY)
    3037                         && enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY)
    3038                     {
    3039                         uint32_t cbIgnored = 0;
    3040                         drvAudioStreamPreBufComitting(pThis, pStreamEx, NULL, 0, &cbIgnored);
    3041                         cFramesLive = PDMAudioPropsBytesToFrames(&pStreamEx->Core.Props, pStreamEx->Out.cbPreBuffered);
    3042                     }
    3043                     else
    3044                         Log3Func(("[%s] Skipping committing pre-buffered samples (status: %#x backend: %s)!\n",
    3045                                   pStreamEx->Core.szName, pStreamEx->fStatus, PDMHostAudioStreamStateGetName(enmBackendState)));
    3046                 }
    3047                 break;
    3048         }
    3049         Log3Func(("[%s] cFramesLive=%RU32\n", pStreamEx->Core.szName, cFramesLive));
    3050         if (cFramesLive == 0)
    3051         {
    3052             /*
    3053              * Tell the backend to start draining the stream, that is,
    3054              * play the remaining buffered data and stop.
    3055              */
    3056             rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DRAIN);
    3057             if (rc == VERR_NOT_SUPPORTED) /* Not all backends support draining yet. */
    3058                 rc = VINF_SUCCESS;
    3059             /** @todo r=bird: We could probably just skip this next check, as if drainig
    3060              *        failes, we should definitely try disable the stream.  Maybe the
    3061              *        host audio device was unplugged and we're leaving this stream in a
    3062              *        bogus state. */
    3063             if (RT_SUCCESS(rc))
    3064             {
    3065                 /*
    3066                  * Before we disable the stream, check if the backend has finished playing the buffered data.
    3067                  */
    3068                 uint32_t cbPending;
    3069                 if (!pThis->pHostDrvAudio || !pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional. */
    3070                     cbPending = 0;
    3071                 else
    3072                 {
    3073                     cbPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pStreamEx->pBackend);
    3074                     Log3Func(("[%s] cbPending=%RU32 (%#RX32)\n", pStreamEx->Core.szName, cbPending, cbPending));
    3075                 }
    3076                 if (cbPending == 0)
    3077                 {
    3078                     /*
    3079                      * Okay, disable it.
    3080                      */
    3081                     LogFunc(("[%s] Disabling pending stream\n", pStreamEx->Core.szName));
     3049                    LogFunc(("Stream '%s': Backend DRAIN failed with %Rrc, disabling the stream instead...\n",
     3050                             pStreamEx->Core.szName));
    30823051                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    3083                     if (RT_SUCCESS(rc))
    3084                     {
    3085                         pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PENDING_DISABLE);
    3086                         PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
    3087                         drvAudioStreamResetInternal(pStreamEx);
    3088                     }
    3089                     /** @todo r=bird: This log entry sounds a rather fishy to be honest...  Any
    3090                      *        backend which would do that, or genuinely need this?  */
    3091                     else
    3092                         LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pStreamEx->Core.szName, rc));
     3052                    AssertRC(rc);
     3053                    drvAudioStreamResetOnDisable(pStreamEx);
    30933054                }
    30943055            }
    30953056        }
     3057        else
     3058            Assert(pStreamEx->Out.enmPlayState == DRVAUDIOPLAYSTATE_NOPLAY);
     3059
     3060        /*
     3061         * Check the backend status to see if it's still draining and to
     3062         * update our status when it stops doing so.
     3063         */
     3064        PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
     3065        if (enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING)
     3066        {
     3067            uint32_t cbIgnored = 0;
     3068            pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, NULL, 0, &cbIgnored);
     3069        }
     3070        else
     3071        {
     3072            LogFunc(("Stream '%s': Backend finished draining.\n", pStreamEx->Core.szName));
     3073            drvAudioStreamResetOnDisable(pStreamEx);
     3074        }
    30963075    }
    30973076
     
    30993078    pStreamEx->nsLastIterated = RTTimeNanoTS();
    31003079
    3101     if (RT_FAILURE(rc))
    3102         LogFunc(("[%s] Failed with %Rrc\n", pStreamEx->Core.szName, rc));
    3103 
    3104     return rc;
     3080    return VINF_SUCCESS; /** @todo r=bird: What can the caller do with an error status here? */
    31053081}
    31063082
     
    32343210     */
    32353211    uint32_t cbWritable = 0;
    3236     DRVAUDIOPLAYSTATE const enmPlayMode = pStreamEx->Out.enmPlayState;
     3212    DRVAUDIOPLAYSTATE const       enmPlayMode     = pStreamEx->Out.enmPlayState;
     3213    PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
    32373214    if (   PDMAudioStrmStatusCanWrite(pStreamEx->fStatus)
    3238         && pThis->pHostDrvAudio != NULL)
     3215        && pThis->pHostDrvAudio != NULL
     3216        && enmBackendState != PDMHOSTAUDIOSTREAMSTATE_DRAINING)
    32393217    {
    32403218        switch (enmPlayMode)
     
    32463224            case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
    32473225                Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
    3248                 Assert(drvAudioStreamGetBackendState(pThis, pStreamEx) == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);
     3226                Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);
    32493227                cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
    32503228                break;
     
    32843262                   to move the data. */
    32853263                Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
    3286                 Assert(drvAudioStreamGetBackendState(pThis, pStreamEx) == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);
     3264                Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);
    32873265                uint32_t const cbMin = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Props, 8);
    32883266                cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     
    33083286
    33093287    RTCritSectLeave(&pThis->CritSect);
    3310     Log3Func(("[%s] cbWritable=%RU32 (%RU64ms) enmPlayMode=%s\n", pStreamEx->Core.szName, cbWritable,
    3311               PDMAudioPropsBytesToMilli(&pStreamEx->Host.Cfg.Props, cbWritable), drvAudioPlayStateName(enmPlayMode) ));
     3288    Log3Func(("[%s] cbWritable=%RU32 (%RU64ms) enmPlayMode=%s enmBackendState=%s\n",
     3289              pStreamEx->Core.szName, cbWritable, PDMAudioPropsBytesToMilli(&pStreamEx->Host.Cfg.Props, cbWritable),
     3290              drvAudioPlayStateName(enmPlayMode), PDMHostAudioStreamStateGetName(enmBackendState) ));
    33123291    return cbWritable;
    33133292}
     
    33493328                && (enmDir == PDMAUDIODIR_IN ? pThis->In.fEnabled : pThis->Out.fEnabled)
    33503329                && (   enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY
     3330                    || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING
    33513331                    || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_INITIALIZING ))
    33523332                enmState = enmDir == PDMAUDIODIR_IN ? PDMAUDIOSTREAMSTATE_ENABLED_READABLE : PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE;
     
    48584838                              STAMUNIT_BYTES, "Total bytes read.");
    48594839#endif
    4860 
    4861     /*
    4862      * Create a timer to do finish closing output streams in PENDING_DISABLE state.
    4863      *
    4864      * The device won't call us again after it has disabled a the stream and this is
    4865      * a real problem for truely cyclic buffer backends like DSound which will just
    4866      * continue to loop and loop if not stopped.
    4867      */
    4868     RTStrPrintf(pThis->szTimerName, sizeof(pThis->szTimerName), "AudioIterate-%u", pDrvIns->iInstance);
    4869     rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_VIRTUAL, drvAudioEmergencyIterateTimer, NULL /*pvUser*/,
    4870                                 0 /*fFlags*/, pThis->szTimerName, &pThis->hTimer);
    4871     AssertRCReturn(rc, rc);
    48724840
    48734841#ifdef VBOX_WITH_AUDIO_ENUM
  • trunk/src/VBox/Devices/Audio/DrvHostAudioAlsa.cpp

    r88966 r88991  
    5656#include <alsa/asoundlib.h>
    5757#include <alsa/control.h> /* For device enumeration. */
     58#include <alsa/version.h>
    5859#include "DrvHostAudioAlsaStubs.h"
    5960
     
    11251126{
    11261127    RT_NOREF(pInterface);
    1127     AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
    1128 
    1129     return PDMHOSTAUDIOSTREAMSTATE_OKAY;
     1128    PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
     1129    AssertPtrReturn(pStreamALSA, PDMHOSTAUDIOSTREAMSTATE_INVALID);
     1130
     1131    PDMHOSTAUDIOSTREAMSTATE enmStreamState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
     1132    snd_pcm_state_t         enmAlsaState   = snd_pcm_state(pStreamALSA->hPCM);
     1133    if (enmAlsaState == SND_PCM_STATE_DRAINING)
     1134        enmStreamState = PDMHOSTAUDIOSTREAMSTATE_DRAINING;
     1135#if (((SND_LIB_MAJOR) << 16) | ((SND_LIB_MAJOR) << 8) | (SND_LIB_SUBMINOR)) >= 0x10002 /* was added in 1.0.2 */
     1136    else if (enmAlsaState == SND_PCM_STATE_DISCONNECTED)
     1137        enmStreamState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
     1138#endif
     1139
     1140    Log5Func(("Stream '%s': ALSA state=%s -> %s\n",
     1141              pStreamALSA->Cfg.szName, snd_pcm_state_name(enmAlsaState), PDMHostAudioStreamStateGetName(enmStreamState) ));
     1142    return enmStreamState;
    11301143}
    11311144
     
    12741287    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    12751288    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
    1276     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    1277     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    12781289    AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
    12791290    Log4Func(("@%#RX64: pvBuf=%p cbBuf=%#x (%u) state=%s - %s\n", pStreamALSA->offInternal, pvBuf, cbBuf, cbBuf,
    12801291              snd_pcm_state_name(snd_pcm_state(pStreamALSA->hPCM)), pStreamALSA->Cfg.szName));
     1292    if (cbBuf)
     1293        AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     1294    else
     1295    {
     1296        /* Fend off draining calls. */
     1297        *pcbWritten = 0;
     1298        return VINF_SUCCESS;
     1299    }
    12811300
    12821301    /*
  • trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp

    r88959 r88991  
    224224    /** The Direct Sound capturing interface. */
    225225    LPDIRECTSOUNDCAPTURE8       pDSC;
    226     /** A drain stop timer that makes sure a draining stream will be
    227      *  properly stopped (seem the non-loop mode is a bit buggy). */
    228     TMTIMERHANDLE               hDrainTimer;
    229226    /** List of streams (DSOUNDSTREAM).
    230227     * Requires CritSect ownership.  */
     
    19631960        if (pStreamDS->Out.pDSB)
    19641961        {
    1965             /* Don't stop draining buffers. They'll stop by themselves. */
    1966             if (pStreamDS->Out.fDrain)
    1967                 LogFunc(("Stream '%s' is draining\n", pStreamDS->Cfg.szName));
    1968             else
    1969             {
    1970                 rc = drvHostDSoundStreamStopPlayback(pThis, pStreamDS, true /*fReset*/);
    1971                 if (RT_SUCCESS(rc))
    1972                     LogRel3(("DSound: Stopped playback on '%s'.\n", pStreamDS->Cfg.szName));
    1973             }
     1962            rc = drvHostDSoundStreamStopPlayback(pThis, pStreamDS, true /*fReset*/);
     1963            if (RT_SUCCESS(rc))
     1964                LogRel3(("DSound: Stopped playback on '%s'.\n", pStreamDS->Cfg.szName));
    19741965        }
    19751966    }
     
    20972088
    20982089/**
    2099  * This is used by the timer function as well as when arming the timer.
    2100  *
    2101  * @param   pThis       The DSound host audio driver instance data.
    2102  * @param   msNow       A current RTTimeMilliTS() value.
    2103  */
    2104 static void drvHostDSoundDrainTimerWorker(PDRVHOSTDSOUND pThis, uint64_t msNow)
    2105 {
    2106     /*
    2107      * Go thru the stream list and look at draining streams.
    2108      */
    2109     uint64_t        msNext = UINT64_MAX;
    2110     RTCritSectEnter(&pThis->CritSect);
    2111     PDSOUNDSTREAM   pCur;
    2112     RTListForEach(&pThis->HeadStreams, pCur, DSOUNDSTREAM, ListEntry)
    2113     {
    2114         if (   pCur->Cfg.enmDir == PDMAUDIODIR_OUT
    2115             && pCur->Out.fDrain)
    2116         {
    2117             uint64_t const msCurDeadline = pCur->Out.msDrainDeadline;
    2118             if (msCurDeadline > 0 && msCurDeadline < msNext)
    2119             {
    2120                 if (msCurDeadline > msNow)
    2121                     msNext = pCur->Out.msDrainDeadline;
    2122                 else
    2123                 {
    2124                     LogRel2(("DSound: Stopping draining of '%s' {%s} ...\n",
    2125                              pCur->Cfg.szName, drvHostDSoundStreamStatusString(pCur)));
    2126                     if (pCur->Out.pDSB)
    2127                     {
    2128                         HRESULT hrc = IDirectSoundBuffer8_Stop(pCur->Out.pDSB);
    2129                         if (FAILED(hrc))
    2130                             LogRelMax(64, ("DSound: Failed to stop draining stream '%s': %Rhrc\n", pCur->Cfg.szName, hrc));
    2131                     }
    2132                     pCur->Out.fDrain = false;
    2133                 }
    2134             }
    2135         }
    2136     }
    2137 
    2138     /*
    2139      * Re-arm the timer if necessary.
    2140      */
    2141     if (msNext != UINT64_MAX)
    2142         PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hDrainTimer, msNext - msNow);
    2143     RTCritSectLeave(&pThis->CritSect);
    2144 }
    2145 
    2146 
    2147 /**
    2148  * @callback_method_impl{FNTMTIMERDRV,
    2149  * This is a hack to ensure that draining streams stop playing.}
    2150  */
    2151 static DECLCALLBACK(void) drvHostDSoundDrainStopHackTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
    2152 {
    2153     PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
    2154     RT_NOREF(hTimer, pvUser);
    2155     drvHostDSoundDrainTimerWorker(pThis, RTTimeMilliTS());
    2156 }
    2157 
    2158 
    2159 /**
    21602090 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
    21612091 */
     
    21852115                pStreamDS->Out.msDrainDeadline = PDMAudioPropsBytesToMilli(&pStreamDS->Cfg.Props,  pStreamDS->cbBufSize) + msNow;
    21862116                pStreamDS->Out.fDrain          = true;
    2187                 drvHostDSoundDrainTimerWorker(pThis, msNow);
    21882117            }
    21892118            else
     
    24162345    AssertPtrReturn(pStreamDS, PDMHOSTAUDIOSTREAMSTATE_INVALID);
    24172346
    2418     LogFlowFunc(("returns OKAY for '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
    2419     return PDMHOSTAUDIOSTREAMSTATE_OKAY;
     2347    if (   pStreamDS->Cfg.enmDir != PDMAUDIODIR_OUT
     2348        || !pStreamDS->Out.fDrain)
     2349    {
     2350        LogFlowFunc(("returns OKAY for '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
     2351        return PDMHOSTAUDIOSTREAMSTATE_OKAY;
     2352    }
     2353    LogFlowFunc(("returns DRAINING for '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
     2354    return PDMHOSTAUDIOSTREAMSTATE_DRAINING;
    24202355}
    24212356
     
    24302365    PDSOUNDSTREAM   pStreamDS = (PDSOUNDSTREAM)pStream;
    24312366    AssertPtrReturn(pStreamDS, 0);
    2432     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    2433     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     2367    if (cbBuf)
     2368        AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    24342369    AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
    24352370
     
    25292464                     drvHostDSoundStreamStatusString(pStreamDS) ));
    25302465    }
     2466    else if (   pStreamDS->Out.fDrain
     2467             && RTTimeMilliTS() >= pStreamDS->Out.msDrainDeadline)
     2468    {
     2469        LogRel2(("DSound: Stopping draining of '%s' {%s} ...\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
     2470        if (pStreamDS->Out.pDSB)
     2471        {
     2472            HRESULT hrc = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
     2473            if (FAILED(hrc))
     2474                LogRelMax(64, ("DSound: Failed to stop draining stream '%s': %Rhrc\n", pStreamDS->Cfg.szName, hrc));
     2475        }
     2476        pStreamDS->Out.fDrain = false;
     2477        pStreamDS->fEnabled   = false;
     2478    }
     2479
    25312480    return VINF_SUCCESS;
    25322481}
     
    27562705    RTListInit(&pThis->HeadStreams);
    27572706    pThis->pDrvIns                   = pDrvIns;
    2758     pThis->hDrainTimer               = NIL_TMTIMERHANDLE;
    27592707    /* IBase */
    27602708    pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
     
    28422790        LogRel2(("DSound: Notification client is disabled (ver %#RX64)\n", RTSystemGetNtVersion()));
    28432791#endif
    2844 
    2845     /*
    2846      * Create a timer that helps making sure draining streams actually stop playing.
    2847      * (Seem dsound doesn't stop by itself in many cases.)
    2848      */
    2849     int rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_REAL, drvHostDSoundDrainStopHackTimer, NULL /*pvUser*/, 0 /*fFlags*/,
    2850                                     "DSound drain", &pThis->hDrainTimer);
    2851     AssertRCReturn(rc, rc);
    28522792
    28532793    /*
  • trunk/src/VBox/Devices/Audio/DrvHostAudioOss.cpp

    r88958 r88991  
    461461    POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
    462462
     463    /** @todo this might be a little optimisitic...   */
    463464    pStreamOSS->fDraining = false;
    464465
     
    494495    if (pStreamOSS->Cfg.enmDir == PDMAUDIODIR_IN)
    495496        rc = VINF_SUCCESS; /** @todo apparently nothing to do here? */
    496     else if (pStreamOSS->fDraining)
    497     {
    498         LogFlow(("Ignoring stream disable because we're draining...\n"));
    499         rc = VINF_SUCCESS; /** @todo apparently nothing to do here? */
    500     }
    501497    else
    502498    {
     499        /*
     500         * If we're still draining, try kick the thread before we try disable the stream.
     501         */
     502        if (pStreamOSS->fDraining)
     503        {
     504            LogFuncFlow(("Trying to cancel draining...\n"));
     505            if (pStreamOSS->hThreadDrain != NIL_RTTHREAD)
     506            {
     507                RTThreadPoke(pStreamOSS->hThreadDrain);
     508                rc = RTThreadWait(pStreamOSS->hThreadDrain, 1 /*ms*/, NULL);
     509                if (RT_SUCCESS(rc) || rc == VERR_INVALID_HANDLE)
     510                    pStreamOSS->fDraining = false;
     511                else
     512                    LogFuncFlow(("Failed to cancel draining (%Rrc)\n", rc));
     513            }
     514            else
     515            {
     516                LogFuncFlow(("Thread handle is NIL, so we can't be draining\n"));
     517                pStreamOSS->fDraining = false;
     518            }
     519        }
     520
    503521        /** @todo Official documentation says this isn't the right way to stop playback.
    504522         *        It may work in some implementations but fail in all others...  Suggest
     
    710728{
    711729    RT_NOREF(pInterface);
    712     AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
    713     return PDMHOSTAUDIOSTREAMSTATE_OKAY;
     730    POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream;
     731    AssertPtrReturn(pStreamOSS, PDMHOSTAUDIOSTREAMSTATE_INVALID);
     732    if (!pStreamOSS->fDraining)
     733        return PDMHOSTAUDIOSTREAMSTATE_OKAY;
     734    return PDMHOSTAUDIOSTREAMSTATE_DRAINING;
    714735}
    715736
  • trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp

    r88958 r88991  
    12791279            if (enmOpState == PA_OPERATION_RUNNING)
    12801280            {
     1281/** @todo consider corking it immediately instead, as that's what the caller
     1282 *        wants now... */
    12811283                LogFlowFunc(("Drain (%p) already running on '%s', skipping.\n", pStreamPA->pDrainOp, pStreamPA->Cfg.szName));
    12821284                pa_threaded_mainloop_unlock(pThis->pMainLoop);
     
    15581560    PDRVHOSTPULSEAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPULSEAUDIO, IHostAudio);
    15591561    AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
     1562    PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
     1563    AssertPtrReturn(pStreamPA, PDMHOSTAUDIOSTREAMSTATE_INVALID);
    15601564
    15611565    /* Check PulseAudio's general status. */
     1566    PDMHOSTAUDIOSTREAMSTATE enmBackendStreamState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
    15621567    if (pThis->pContext)
    15631568    {
    1564         pa_context_state_t const enmState = pa_context_get_state(pThis->pContext);
    1565         if (PA_CONTEXT_IS_GOOD(enmState))
    1566         {
    1567             /** @todo should we check the actual stream state? */
    1568             return PDMHOSTAUDIOSTREAMSTATE_OKAY;
    1569         }
    1570         LogFunc(("non-good context state: %d\n", enmState));
     1569        pa_context_state_t const enmCtxState = pa_context_get_state(pThis->pContext);
     1570        if (PA_CONTEXT_IS_GOOD(enmCtxState))
     1571        {
     1572            pa_stream_state_t const enmStreamState = pa_stream_get_state(pStreamPA->pStream);
     1573            if (PA_STREAM_IS_GOOD(enmState))
     1574            {
     1575                if (enmState != PA_STREAM_CREATING)
     1576                {
     1577                    if (   pStreamPA->Cfg.enmDir != PDMAUDIODIR_OUT
     1578                        || pStreamPA->pDrainOp == NULL
     1579                        || pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_RUNNING)
     1580                        enmBackendStreamState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
     1581                    else
     1582                        enmBackendStreamState = PDMHOSTAUDIOSTREAMSTATE_DRAINING;
     1583                }
     1584                else
     1585                    enmBackendStreamState = PDMHOSTAUDIOSTREAMSTATE_INITIALIZING;
     1586            }
     1587            else
     1588                LogFunc(("non-good PA stream state: %d\n", enmStreamState));
     1589        }
     1590        else
     1591            LogFunc(("non-good PA context state: %d\n", enmCtxState));
    15711592    }
    15721593    else
    15731594        LogFunc(("No context!\n"));
    1574     return PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
     1595    LogFlowFunc(("returns %s for stream '%s'\n", PDMHostAudioStreamStateGetName(enmBackendStreamState), pStreamPA->Cfg.szName));
     1596    return enmBackendStreamState;
    15751597}
    15761598
     
    15851607    PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
    15861608    AssertPtrReturn(pStreamPA, VERR_INVALID_POINTER);
    1587     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    1588     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    15891609    AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
     1610    if (cbBuf)
     1611        AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     1612    else
     1613    {
     1614        /* Fend off draining calls. */
     1615        *pcbWritten = 0;
     1616        return VINF_SUCCESS;
     1617    }
    15901618
    15911619    pa_threaded_mainloop_lock(pThis->pMainLoop);
  • trunk/src/VBox/Devices/Audio/DrvHostAudioWasApi.cpp

    r88959 r88991  
    252252    IMMDevice                      *pIDeviceOutput;
    253253
    254     /** A drain stop timer that makes sure a draining stream will be properly
    255      * stopped (mainly for clean state and to reduce resource usage). */
    256     TMTIMERHANDLE                   hDrainTimer;
    257254    /** List of streams (DRVHOSTAUDIOWASSTREAM).
    258255     * Requires CritSect ownership.  */
     
    21402137
    21412138    int rc = VINF_SUCCESS;
    2142     if (!pStreamWas->fDraining)
    2143     {
    2144         if (pStreamWas->fStarted)
    2145         {
    2146             HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop();
    2147             LogFlowFunc(("Stop(%s) returns %Rhrc\n", pStreamWas->Cfg.szName, hrc));
    2148             if (FAILED(hrc))
    2149             {
    2150                 LogRelMax(64, ("WasAPI: Stopping '%s' failed (disable): %Rhrc\n", pStreamWas->Cfg.szName, hrc));
    2151                 rc = VERR_GENERAL_FAILURE;
    2152             }
    2153             pStreamWas->fStarted = false;
    2154         }
    2155     }
    2156     else
    2157     {
    2158         LogFunc(("Stream '%s' is still draining...\n", pStreamWas->Cfg.szName));
    2159         Assert(pStreamWas->fStarted);
     2139    if (pStreamWas->fStarted)
     2140    {
     2141        HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop();
     2142        LogFlowFunc(("Stop(%s) returns %Rhrc\n", pStreamWas->Cfg.szName, hrc));
     2143        if (FAILED(hrc))
     2144        {
     2145            LogRelMax(64, ("WasAPI: Stopping '%s' failed (disable): %Rhrc\n", pStreamWas->Cfg.szName, hrc));
     2146            rc = VERR_GENERAL_FAILURE;
     2147        }
     2148        pStreamWas->fStarted  = false;
     2149        pStreamWas->fDraining = false;
    21602150    }
    21612151
     
    22412231
    22422232/**
    2243  * This is used by the timer function as well as when arming the timer.
    2244  *
    2245  * @param   pThis       The DSound host audio driver instance data.
    2246  * @param   msNow       A current RTTimeMilliTS() value.
    2247  */
    2248 static void drvHostWasDrainTimerWorker(PDRVHOSTAUDIOWAS pThis, uint64_t msNow)
    2249 {
    2250     /*
    2251      * Go thru the stream list and look at draining streams.
    2252      */
    2253     uint64_t        msNext = UINT64_MAX;
    2254     RTCritSectRwEnterShared(&pThis->CritSectStreamList);
    2255     PDRVHOSTAUDIOWASSTREAM   pCur;
    2256     RTListForEach(&pThis->StreamHead, pCur, DRVHOSTAUDIOWASSTREAM, ListEntry)
    2257     {
    2258         if (   pCur->fDraining
    2259             && pCur->Cfg.enmDir == PDMAUDIODIR_OUT)
    2260         {
    2261             Assert(pCur->fStarted);
    2262             uint64_t msCurDeadline = pCur->msDrainDeadline;
    2263             if (msCurDeadline > 0 && msCurDeadline < msNext)
    2264             {
    2265                 /* Take the lock and recheck: */
    2266                 RTCritSectEnter(&pCur->CritSect);
    2267                 msCurDeadline = pCur->msDrainDeadline;
    2268                 if (   pCur->fDraining
    2269                     && msCurDeadline > 0
    2270                     && msCurDeadline < msNext)
    2271                 {
    2272                     if (msCurDeadline > msNow)
    2273                         msNext = pCur->msDrainDeadline;
    2274                     else
    2275                     {
    2276                         LogRel2(("WasAPI: Stopping draining of '%s' {%s} ...\n",
    2277                                  pCur->Cfg.szName, drvHostWasStreamStatusString(pCur)));
    2278                         HRESULT hrc = pCur->pDevCfg->pIAudioClient->Stop();
    2279                         if (FAILED(hrc))
    2280                             LogRelMax(64, ("WasAPI: Failed to stop draining stream '%s': %Rhrc\n", pCur->Cfg.szName, hrc));
    2281                         pCur->fDraining = false;
    2282                         pCur->fStarted  = false;
    2283                     }
    2284                 }
    2285                 RTCritSectLeave(&pCur->CritSect);
    2286             }
    2287         }
    2288     }
    2289 
    2290     /*
    2291      * Re-arm the timer if necessary.
    2292      */
    2293     if (msNext != UINT64_MAX)
    2294         PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hDrainTimer, msNext - msNow);
    2295     RTCritSectRwLeaveShared(&pThis->CritSectStreamList);
    2296 }
    2297 
    2298 
    2299 /**
    2300  * @callback_method_impl{FNTMTIMERDRV,
    2301  * This is to ensure that draining streams stop properly.}
    2302  */
    2303 static DECLCALLBACK(void) drvHostWasDrainStopTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
    2304 {
    2305     PDRVHOSTAUDIOWAS pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTAUDIOWAS);
    2306     RT_NOREF(hTimer, pvUser);
    2307     drvHostWasDrainTimerWorker(pThis, RTTimeMilliTS());
    2308 }
    2309 
    2310 
    2311 /**
    23122233 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
    23132234 */
    23142235static DECLCALLBACK(int) drvHostAudioWasHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    23152236{
    2316     PDRVHOSTAUDIOWAS        pThis      = RT_FROM_MEMBER(pInterface, DRVHOSTAUDIOWAS, IHostAudio);
     2237    RT_NOREF(pInterface);
    23172238    PDRVHOSTAUDIOWASSTREAM  pStreamWas = (PDRVHOSTAUDIOWASSTREAM)pStream;
    23182239    AssertReturn(pStreamWas->Cfg.enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
     
    23232244    /*
    23242245     * If the stram was started, calculate when the buffered data has finished
    2325      * playing and switch to drain mode.  Use the drain timer callback worker
    2326      * to re-arm the timer or to stop the playback.
     2246     * playing and switch to drain mode.  DrvAudio will keep on calling
     2247     * pfnStreamPlay with an empty buffer while we're draining, so we'll use
     2248     * that for checking the deadline and finally stopping the stream.
    23272249     */
    23282250    RTCritSectEnter(&pStreamWas->CritSect);
     
    23632285    RTCritSectLeave(&pStreamWas->CritSect);
    23642286
    2365     /*
    2366      * Always do drain timer processing to re-arm the timer or actually stop
    2367      * this stream (and others).  (Must be done _after_ unlocking the stream.)
    2368      */
    2369     drvHostWasDrainTimerWorker(pThis, RTTimeMilliTS());
    2370 
    23712287    LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostWasStreamStatusString(pStreamWas)));
    23722288    return rc;
     
    25372453    {
    25382454        if (RT_SUCCESS(pStreamWas->pDevCfg->rcSetup))
    2539             enmState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
     2455        {
     2456            if (!pStreamWas->fDraining)
     2457                enmState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
     2458            else
     2459            {
     2460                Assert(pStreamWas->Cfg.enmDir == PDMAUDIODIR_OUT);
     2461                enmState = PDMHOSTAUDIOSTREAMSTATE_DRAINING;
     2462            }
     2463        }
    25402464        else if (   pStreamWas->pDevCfg->rcSetup == VERR_AUDIO_STREAM_INIT_IN_PROGRESS
    25412465                 || pStreamWas->fSwitchingDevice )
     
    25632487    PDRVHOSTAUDIOWASSTREAM  pStreamWas = (PDRVHOSTAUDIOWASSTREAM)pStream;
    25642488    AssertPtrReturn(pStreamWas, VERR_INVALID_POINTER);
    2565     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    2566     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    25672489    AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
     2490    if (cbBuf)
     2491        AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    25682492    Assert(PDMAudioPropsIsSizeAligned(&pStreamWas->Cfg.Props, cbBuf));
    25692493
     
    26152539        uint32_t const cFramesToWrite = PDMAudioPropsBytesToFrames(&pStreamWas->Cfg.Props, cbToWrite);
    26162540        Assert(PDMAudioPropsFramesToBytes(&pStreamWas->Cfg.Props, cFramesToWrite) == cbToWrite);
    2617         Log3Func(("@%RX64: cFramesPending=%#x -> cbWritable=%#x cbToWrite=%#x cFramesToWrite=%#x {%s}\n",
     2541        Log3Func(("@%#RX64: cFramesPending=%#x -> cbWritable=%#x cbToWrite=%#x cFramesToWrite=%#x {%s}\n",
    26182542                  pStreamWas->offInternal, cFramesPending, cbWritable, cbToWrite, cFramesToWrite,
    26192543                  drvHostWasStreamStatusString(pStreamWas) ));
     
    26732597
    26742598    /*
     2599     * Do draining deadline processing.
     2600     */
     2601    uint64_t const msNow = RTTimeMilliTS();
     2602    if (   !pStreamWas->fDraining
     2603        || msNow < pStreamWas->msDrainDeadline)
     2604    { /* likely */ }
     2605    else
     2606    {
     2607        LogRel2(("WasAPI: Stopping draining of '%s' {%s} ...\n", pStreamWas->Cfg.szName, drvHostWasStreamStatusString(pStreamWas)));
     2608        HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop();
     2609        if (FAILED(hrc))
     2610            LogRelMax(64, ("WasAPI: Failed to stop draining stream '%s': %Rhrc\n", pStreamWas->Cfg.szName, hrc));
     2611        pStreamWas->fDraining = false;
     2612        pStreamWas->fStarted  = false;
     2613    }
     2614
     2615    /*
    26752616     * Done.
    26762617     */
    26772618    uint64_t const msPrev = pStreamWas->msLastTransfer;
    2678     uint64_t const msNow  = RTTimeMilliTS();
    26792619    if (cbWritten)
    26802620        pStreamWas->msLastTransfer = msNow;
     
    29962936     */
    29972937    pThis->pDrvIns                          = pDrvIns;
    2998     pThis->hDrainTimer                      = NIL_TMTIMERHANDLE;
    29992938    pThis->hEvtCachePurge                   = NIL_RTSEMEVENTMULTI;
    30002939#if 0
     
    31393078
    31403079    pThis->pNotifyClient->lockLeave();
    3141 
    3142     /*
    3143      * We need a timer for draining streams.
    3144      */
    3145     rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_REAL, drvHostWasDrainStopTimer, NULL /*pvUser*/, 0 /*fFlags*/,
    3146                                 "WasAPI drain", &pThis->hDrainTimer);
    3147     AssertRCReturn(rc, rc);
    31483080
    31493081#if 0
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