VirtualBox

Changeset 88760 in vbox


Ignore:
Timestamp:
Apr 29, 2021 12:54:45 AM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
144090
Message:

DrvAudio: Working on support for asynchronous stream backend init and smoother device switch. bugref:9890

Location:
trunk
Files:
4 edited

Legend:

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

    r88731 r88760  
    903903 * @{ */
    904904/** No flags being set. */
    905 #define PDMAUDIOSTREAM_STS_NONE             UINT32_C(0)
     905#define PDMAUDIOSTREAM_STS_NONE                 UINT32_C(0)
    906906/** Set if the backend for the stream has been initialized.
    907  * This is generally always set after stream creation, but can be cleared if the
    908  * re-initialization of the stream fails later on. */
    909 #define PDMAUDIOSTREAM_STS_INITIALIZED      RT_BIT_32(0)
     907 *
     908 * PDMIAUDIOCONNECTOR: This is generally always set after stream creation, but
     909 * can be cleared if the re-initialization of the stream fails later on.
     910 *
     911 * PDMIHOSTAUDIO: This may not be set immediately if the backend is doing some
     912 * of the stream creation asynchronously.  The DrvAudio code will not report
     913 * this to the devices, but keep on prebuffering till it is set. */
     914#define PDMAUDIOSTREAM_STS_INITIALIZED          RT_BIT_32(0)
    910915/** Set if the stream is enabled, clear if disabled. */
    911 #define PDMAUDIOSTREAM_STS_ENABLED          RT_BIT_32(1)
     916#define PDMAUDIOSTREAM_STS_ENABLED              RT_BIT_32(1)
    912917/** Set if the stream is paused.
    913918 * Requires enabled status to be set when used. */
    914 #define PDMAUDIOSTREAM_STS_PAUSED           RT_BIT_32(2)
     919#define PDMAUDIOSTREAM_STS_PAUSED               RT_BIT_32(2)
    915920/** Output only: Set when the stream is draining.
    916  * Requires the enabled status to be set when used. */
    917 #define PDMAUDIOSTREAM_STS_PENDING_DISABLE  RT_BIT_32(3)
    918 /** Set if the stream needs to be re-initialized by the device (i.e. call
    919  * PDMIAUDIOCONNECTOR::pfnStreamReInit).
    920  * (The other status bits are preserved and are worked as normal while in this
    921  * state, so that the stream can resume operation where it left off.)
    922  * @note This is not appropriate for PDMIHOSTAUDIO::pfnStreamGetStatus.  */
    923 #define PDMAUDIOSTREAM_STS_NEED_REINIT      RT_BIT_32(4)
    924 /** Validation mask. */
    925 #define PDMAUDIOSTREAM_STS_VALID_MASK       UINT32_C(0x0000001f)
    926 /** Asserts the validity of the given stream status mask.   */
     921 * Requires the enabled status to be set when used.
     922 * @todo See todo in drvAudioStreamPlay() regarding the suitability of this
     923 *       for PDMIHOSTAUDIO. */
     924#define PDMAUDIOSTREAM_STS_PENDING_DISABLE      RT_BIT_32(3)
     925
     926/** PDMIAUDIOCONNECTOR: Set if the stream needs to be re-initialized by the
     927 * device (i.e. call PDMIAUDIOCONNECTOR::pfnStreamReInit). (The other status
     928 * bits are preserved and are worked as normal while in this state, so that the
     929 * stream can resume operation where it left off.)  */
     930#define PDMAUDIOSTREAM_STS_NEED_REINIT          RT_BIT_32(8)
     931/** Validation mask for PDMIAUDIOCONNECTOR. */
     932#define PDMAUDIOSTREAM_STS_VALID_MASK           UINT32_C(0x0000010f)
     933/** Asserts the validity of the given stream status mask for PDMIAUDIOCONNECTOR. */
    927934#define PDMAUDIOSTREAM_STS_ASSERT_VALID(a_fStreamStatus) do { \
    928935        AssertMsg(!((a_fStreamStatus) & ~PDMAUDIOSTREAM_STS_VALID_MASK), ("%#x\n", (a_fStreamStatus))); \
     936        Assert(!((a_fStreamStatus) & PDMAUDIOSTREAM_STS_PAUSED)          || ((a_fStreamStatus) & PDMAUDIOSTREAM_STS_ENABLED)); \
     937        Assert(!((a_fStreamStatus) & PDMAUDIOSTREAM_STS_PENDING_DISABLE) || ((a_fStreamStatus) & PDMAUDIOSTREAM_STS_ENABLED)); \
     938    } while (0)
     939
     940/** PDMIHOSTAUDIO: Backend is preparing a device switch, DrvAudio should
     941 * pre-buffer to make that smoother and quicker.
     942 * Call PDMIAUDIONOTIFYFROMHOST::pfnStreamNotifyDeviceChanged when clearing. */
     943#define PDMAUDIOSTREAM_STS_PREPARING_SWITCH     RT_BIT_32(16)
     944/** Validation mask for PDMIHOSTAUDIO. */
     945#define PDMAUDIOSTREAM_STS_VALID_MASK_BACKEND   UINT32_C(0x0001000f)
     946/** Asserts the validity of the given stream status mask for PDMIHOSTAUDIO. */
     947#define PDMAUDIOSTREAM_STS_ASSERT_VALID_BACKEND(a_fStreamStatus) do { \
     948        AssertMsg(!((a_fStreamStatus) & ~PDMAUDIOSTREAM_STS_VALID_MASK_BACKEND), ("%#x\n", (a_fStreamStatus))); \
    929949        Assert(!((a_fStreamStatus) & PDMAUDIOSTREAM_STS_PAUSED)          || ((a_fStreamStatus) & PDMAUDIOSTREAM_STS_ENABLED)); \
    930950        Assert(!((a_fStreamStatus) & PDMAUDIOSTREAM_STS_PENDING_DISABLE) || ((a_fStreamStatus) & PDMAUDIOSTREAM_STS_ENABLED)); \
     
    979999     *  Only can be destroyed when the reference count reaches 0. */
    9801000    uint32_t volatile       cRefs;
    981     /** Stream status flag. */
    982     uint32_t       fStatus;
     1001    /** Stream status - PDMAUDIOSTREAM_STS_XXX. */
     1002    uint32_t                fStatus;
    9831003    /** Audio direction of this stream. */
    9841004    PDMAUDIODIR             enmDir;
     
    13251345
    13261346    /**
     1347     * Called from PDMIAUDIONOTIFYFROMHOST::pfnNotifyDeviceChanged so the backend
     1348     * can start the device change for a stream.
     1349     *
     1350     * This is mainly to avoid the need for a list of streams in the backend.
     1351     *
     1352     * @param   pInterface          Pointer to this interface.
     1353     * @param   pStream             Pointer to audio stream.
     1354     * @param   pvUser              Backend specific parameter from the call to
     1355     *                              PDMIAUDIONOTIFYFROMHOST::pfnNotifyDeviceChanged.
     1356     */
     1357    DECLR3CALLBACKMEMBER(void, pfnStreamNotifyDeviceChanged,(PPDMIHOSTAUDIO pInterface,
     1358                                                             PPDMAUDIOBACKENDSTREAM pStream, void *pvUser));
     1359
     1360    /**
    13271361     * Controls an audio stream.
    13281362     *
     
    14151449
    14161450/** PDMIHOSTAUDIO interface ID. */
    1417 #define PDMIHOSTAUDIO_IID                           "109d8c74-dfed-4056-b5ad-022de4d249c2"
     1451#define PDMIHOSTAUDIO_IID                           "faab0061-c3c8-481e-b875-abbe81baf94a"
    14181452
    14191453
     
    14291463{
    14301464    /**
     1465     * The device for the given direction changed.
     1466     *
     1467     * The driver above backend (DrvAudio) will call the backend back
     1468     * (PDMIHOSTAUDIO::pfnStreamNotifyDeviceChanged) for all open streams in the
     1469     * given direction. (This ASSUMES the backend uses one output device and one
     1470     * input devices for all streams.)
     1471     *
     1472     * @param   pInterface  Pointer to this interface.
     1473     * @param   enmDir      The audio direction.
     1474     * @param   pvUser      Backend specific parameter for
     1475     *                      PDMIHOSTAUDIO::pfnStreamNotifyDeviceChanged.
     1476     */
     1477    DECLR3CALLBACKMEMBER(void, pfnNotifyDeviceChanged,(PPDMIAUDIONOTIFYFROMHOST pInterface, PDMAUDIODIR enmDir, void *pvUser));
     1478
     1479    /**
     1480     * The stream has changed its device and left the
     1481     * PDMAUDIOSTREAM_STS_PREPARING_SWITCH state.
     1482     *
     1483     * @param   pInterface  Pointer to this interface.
     1484     * @param   pStream     The stream that changed device (backend variant).
     1485     * @param   fReInit     Set if a re-init is required, clear if not.
     1486     */
     1487    DECLR3CALLBACKMEMBER(void, pfnStreamNotifyDeviceChanged,(PPDMIAUDIONOTIFYFROMHOST pInterface,
     1488                                                             PPDMAUDIOBACKENDSTREAM pStream, bool fReInit));
     1489
     1490    /**
    14311491     * One or more audio devices have changed in some way.
    14321492     *
    14331493     * The upstream driver/device should re-evaluate the devices they're using.
    14341494     *
    1435      * @param   pInterface          Pointer to this interface.
     1495     * @todo r=bird: The upstream driver/device does not know which host audio
     1496     *       devices they are using.  This is mainly for triggering enumeration and
     1497     *       logging of the audio devices.
     1498     *
     1499     * @param   pInterface  Pointer to this interface.
    14361500     */
    14371501    DECLR3CALLBACKMEMBER(void, pfnNotifyDevicesChanged,(PPDMIAUDIONOTIFYFROMHOST pInterface));
     
    14391503
    14401504/** PDMIAUDIONOTIFYFROMHOST interface ID. */
    1441 #define PDMIAUDIONOTIFYFROMHOST_IID                 "ec10f36b-ec2d-4b97-9044-2a59fba837ad"
     1505#define PDMIAUDIONOTIFYFROMHOST_IID                 "603f9d72-4b8b-4e0a-aa00-a76982931039"
    14421506
    14431507/** @} */
  • trunk/include/VBox/vmm/pdmaudioinline.h

    r88731 r88760  
    423423 * @returns @c true if ready to be read from, @c false if not.
    424424 * @param   fStatus     Stream status to evaluate, PDMAUDIOSTREAM_STS_XXX.
     425 * @note    Not for backend statuses (use PDMAudioStrmStatusBackendCanRead)!
    425426 */
    426427DECLINLINE(bool) PDMAudioStrmStatusCanRead(uint32_t fStatus)
     
    428429    PDMAUDIOSTREAM_STS_ASSERT_VALID(fStatus);
    429430    AssertReturn(!(fStatus & ~PDMAUDIOSTREAM_STS_VALID_MASK), false);
     431    return (fStatus & (  PDMAUDIOSTREAM_STS_INITIALIZED
     432                       | PDMAUDIOSTREAM_STS_ENABLED
     433                       | PDMAUDIOSTREAM_STS_PAUSED
     434                       | PDMAUDIOSTREAM_STS_NEED_REINIT))
     435        == (  PDMAUDIOSTREAM_STS_INITIALIZED
     436            | PDMAUDIOSTREAM_STS_ENABLED);
     437}
     438
     439/**
     440 * Checks if the stream status is one that can be read from.
     441 *
     442 * @returns @c true if ready to be read from, @c false if not.
     443 * @param   fStatus     Stream status to evaluate, PDMAUDIOSTREAM_STS_XXX.
     444 * @note    Only for backend statuses.
     445 */
     446DECLINLINE(bool) PDMAudioStrmStatusBackendCanRead(uint32_t fStatus)
     447{
     448    PDMAUDIOSTREAM_STS_ASSERT_VALID_BACKEND(fStatus);
     449    AssertReturn(!(fStatus & ~PDMAUDIOSTREAM_STS_VALID_MASK_BACKEND), false);
    430450    return (fStatus & (  PDMAUDIOSTREAM_STS_INITIALIZED
    431451                       | PDMAUDIOSTREAM_STS_ENABLED
     
    441461 * @returns @c true if ready to be written to, @c false if not.
    442462 * @param   fStatus     Stream status to evaluate, PDMAUDIOSTREAM_STS_XXX.
     463 * @note    Not for backend statuses (use PDMAudioStrmStatusBackendCanWrite)!
    443464 */
    444465DECLINLINE(bool) PDMAudioStrmStatusCanWrite(uint32_t fStatus)
     
    456477
    457478/**
     479 * Checks if the stream status is one that can be written to, backend edition.
     480 *
     481 * @returns @c true if ready to be written to, @c false if not.
     482 * @param   fStatus     Stream status to evaluate, PDMAUDIOSTREAM_STS_XXX.
     483 * @note    Only for backend statuses.
     484 */
     485DECLINLINE(bool) PDMAudioStrmStatusBackendCanWrite(uint32_t fStatus)
     486{
     487    PDMAUDIOSTREAM_STS_ASSERT_VALID_BACKEND(fStatus);
     488    AssertReturn(!(fStatus & ~PDMAUDIOSTREAM_STS_VALID_MASK_BACKEND), false);
     489    return (fStatus & (  PDMAUDIOSTREAM_STS_INITIALIZED
     490                       | PDMAUDIOSTREAM_STS_ENABLED
     491                       | PDMAUDIOSTREAM_STS_PAUSED
     492                       | PDMAUDIOSTREAM_STS_PENDING_DISABLE))
     493        == (  PDMAUDIOSTREAM_STS_INITIALIZED
     494            | PDMAUDIOSTREAM_STS_ENABLED);
     495}
     496
     497/**
    458498 * Checks if the stream status is a read-to-operate one.
    459499 *
    460500 * @returns @c true if ready to operate, @c false if not.
    461501 * @param   fStatus     Stream status to evaluate, PDMAUDIOSTREAM_STS_XXX.
     502 * @note    Not for backend statuses!
    462503 */
    463504DECLINLINE(bool) PDMAudioStrmStatusIsReady(uint32_t fStatus)
  • trunk/src/VBox/Devices/Audio/DrvAudio.cpp

    r88733 r88760  
    6262
    6363/**
     64 * Play state of a stream wrt backend.
     65 */
     66typedef enum DRVAUDIOPLAYSTATE
     67{
     68    /** Invalid zero value.   */
     69    DRVAUDIOPLAYSTATE_INVALID = 0,
     70    /** No playback or pre-buffering.   */
     71    DRVAUDIOPLAYSTATE_NOPLAY,
     72    /** Playing w/o any prebuffering. */
     73    DRVAUDIOPLAYSTATE_PLAY,
     74    /** Parallel pre-buffering prior to a device switch (i.e. we're outputting to
     75     * the old device and pre-buffering the same data in parallel). */
     76    DRVAUDIOPLAYSTATE_PLAY_PREBUF,
     77    /** Initial pre-buffering or the pre-buffering for a device switch (if it
     78     * the device setup took less time than filling up the pre-buffer). */
     79    DRVAUDIOPLAYSTATE_PREBUF,
     80    /** The device initialization is taking too long, pre-buffering wraps around
     81     * and drops samples. */
     82    DRVAUDIOPLAYSTATE_PREBUF_OVERDUE,
     83    /** Same as play-prebuf, but we don't have a working output device any more. */
     84    DRVAUDIOPLAYSTATE_PREBUF_SWITCHING,
     85    /** Working on committing the pre-buffered data.
     86     * We'll typically leave this state immediately and go to PLAY, however if
     87     * the backend cannot handle all the pre-buffered data at once, we'll stay
     88     * here till it does. */
     89    DRVAUDIOPLAYSTATE_PREBUF_COMMITTING,
     90    /** End of valid values. */
     91    DRVAUDIOPLAYSTATE_END
     92} DRVAUDIOPLAYSTATE;
     93
     94
     95/**
    6496 * Extended stream structure.
    6597 */
     
    82114    PPDMAUDIOBACKENDSTREAM pBackend;
    83115
    84     /** For output streams this indicates whether the stream has reached
    85      *  its playback threshold, e.g. is playing audio.
    86      *  For input streams this  indicates whether the stream has enough input
    87      *  data to actually start reading audio. */
    88     bool                fThresholdReached;
    89116    /** Do not use the mixing buffers (Guest::MixBuf, Host::MixBuf). */
    90117    bool                fNoMixBufs;
    91     bool                afPadding[2];
     118    bool                afPadding[3];
    92119
    93120    /** Number of (re-)tries while re-initializing the stream. */
    94121    uint32_t            cTriesReInit;
     122
     123    /** The backend status at the last play or capture call.
     124     * This is used to detect state changes.  */
     125    uint32_t            fLastBackendStatus;
    95126
    96127    /** The guest side of the stream. */
     
    162193            /** The size of the pre-buffer allocation (in bytes). */
    163194            uint32_t            cbPreBufAlloc;
     195            /** The current pre-buffering read offset. */
     196            uint32_t            offPreBuf;
    164197            /** Number of bytes we've prebuffered. */
    165198            uint32_t            cbPreBuffered;
    166199            /** The pre-buffering threshold expressed in bytes. */
    167200            uint32_t            cbPreBufThreshold;
    168 
    169             /** Hack alert: Max writable amount reported by the backend.
    170              * This is used to aid buffer underrun detection in DrvAudio while playing.
    171              * Ideally, the backend should have a method for querying number of buffered
    172              * bytes instead.  However this will do for now. */
    173             uint32_t            cbBackendMaxWritable;
     201            /** The play state. */
     202            DRVAUDIOPLAYSTATE   enmPlayState;
    174203        } Out;
    175204    } RT_UNION_NM(u);
     
    230259    /** Friendly name of the driver. */
    231260    char                    szName[64];
    232     /** Critical section for serializing access. */
     261    /** Critical section for serializing access.
     262     * @todo r=bird: This needs to be split up and introduce stream-level locking so
     263     *       that different AIO threads can work in parallel (e.g. input &
     264     *       output, or two output streams).  Maybe put a critect in
     265     *       PDMAUDIOSTREAM? */
    233266    RTCRITSECT              CritSect;
    234267    /** Shutdown indicator. */
    235268    bool                    fTerminate;
    236269#ifdef VBOX_WITH_AUDIO_ENUM
    237     /** Flag indicating to perform an (re-)enumeration of the host audio devices. */
     270    /** Flag indicating that we need to enumerate and log of the host
     271     * audio devices again. */
    238272    bool                    fEnumerateDevices;
    239273#endif
     
    307341
    308342
    309 #ifndef VBOX_AUDIO_TESTCASE
    310 # ifdef LOG_ENABLED
     343#ifdef LOG_ENABLED
    311344
    312345/** Buffer size for dbgAudioStreamStatusToStr.  */
    313 #  define DRVAUDIO_STATUS_STR_MAX sizeof("INITIALIZED ENABLED PAUSED PENDING_DISABLED PENDING_REINIT 0x12345678")
     346# define DRVAUDIO_STATUS_STR_MAX sizeof("INITIALIZED ENABLED PAUSED PENDING_DISABLED NEED_REINIT PREPARING_SWITCH 0x12345678")
    314347
    315348/**
     
    330363    } s_aFlags[] =
    331364    {
    332         { RT_STR_TUPLE("INITIALIZED "),     PDMAUDIOSTREAM_STS_INITIALIZED     },
    333         { RT_STR_TUPLE("ENABLED "),         PDMAUDIOSTREAM_STS_ENABLED         },
    334         { RT_STR_TUPLE("PAUSED "),          PDMAUDIOSTREAM_STS_PAUSED          },
    335         { RT_STR_TUPLE("PENDING_DISABLE "), PDMAUDIOSTREAM_STS_PENDING_DISABLE },
    336         { RT_STR_TUPLE("NEED_REINIT "),     PDMAUDIOSTREAM_STS_NEED_REINIT     },
     365        { RT_STR_TUPLE("INITIALIZED "),      PDMAUDIOSTREAM_STS_INITIALIZED      },
     366        { RT_STR_TUPLE("ENABLED "),          PDMAUDIOSTREAM_STS_ENABLED          },
     367        { RT_STR_TUPLE("PAUSED "),           PDMAUDIOSTREAM_STS_PAUSED           },
     368        { RT_STR_TUPLE("PENDING_DISABLE "),  PDMAUDIOSTREAM_STS_PENDING_DISABLE  },
     369        { RT_STR_TUPLE("NEED_REINIT "),      PDMAUDIOSTREAM_STS_NEED_REINIT      },
     370        { RT_STR_TUPLE("PREPARING_SWITCH "), PDMAUDIOSTREAM_STS_PREPARING_SWITCH },
    337371    };
    338372    if (!fStatus)
     
    359393}
    360394
    361 # endif /* defined(LOG_ENABLED) */
    362 #endif /* !VBOX_AUDIO_TESTCASE */
     395#endif /* defined(LOG_ENABLED) */
     396
     397/**
     398 * Get pre-buffer state name string.
     399 */
     400static const char *drvAudioPlayStateName(DRVAUDIOPLAYSTATE enmState)
     401{
     402    switch (enmState)
     403    {
     404        case DRVAUDIOPLAYSTATE_INVALID:             return "INVALID";
     405        case DRVAUDIOPLAYSTATE_NOPLAY:              return "NOPLAY";
     406        case DRVAUDIOPLAYSTATE_PLAY:                return "PLAY";
     407        case DRVAUDIOPLAYSTATE_PLAY_PREBUF:         return "PLAY_PREBUF";
     408        case DRVAUDIOPLAYSTATE_PREBUF:              return "PREBUF";
     409        case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:      return "PREBUF_OVERDUE";
     410        case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:    return "PREBUF_SWITCHING";
     411        case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:   return "PREBUF_COMMITTING";
     412        case DRVAUDIOPLAYSTATE_END:
     413            break;
     414    }
     415    return "BAD";
     416}
     417
     418
     419/**
     420 * Wrapper around PDMIHOSTAUDIO::pfnStreamGetStatus and checks the result.
     421 *
     422 * @returns PDMAUDIOSTREAM_STS_XXX
     423 * @param   pThis       Pointer to the DrvAudio instance data.
     424 * @param   pStreamEx   The stream to get the backend status for.
     425 */
     426DECLINLINE(uint32_t) drvAudioStreamGetBackendStatus(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
     427{
     428    Assert(pThis->pHostDrvAudio);
     429    uint32_t fBackendStatus = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStreamEx->pBackend);
     430    PDMAUDIOSTREAM_STS_ASSERT_VALID_BACKEND(fBackendStatus);
     431    return fBackendStatus;
     432}
    363433
    364434
     
    914984                    VERR_INVALID_PARAMETER);
    915985
    916     pStreamEx->Core.fStatus |= PDMAUDIOSTREAM_STS_INITIALIZED;
     986    pStreamEx->fLastBackendStatus = drvAudioStreamGetBackendStatus(pThis, pStreamEx);
     987    pStreamEx->Core.fStatus      |= PDMAUDIOSTREAM_STS_INITIALIZED;
    917988    PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->Core.fStatus);
    918989
     
    10611132        Assert(pStreamEx->Out.cbPreBufThreshold == 0);
    10621133        Assert(pStreamEx->Out.cbPreBuffered == 0);
     1134        Assert(pStreamEx->Out.offPreBuf == 0);
    10631135        if (CfgHostAcq.Backend.cFramesPreBuffering != 0)
    10641136        {
    10651137            pStreamEx->Out.cbPreBufThreshold = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering);
    1066             pStreamEx->Out.cbPreBufAlloc = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize - 2);
    1067             pStreamEx->Out.cbPreBufAlloc = RT_MIN(RT_ALIGN_32(pStreamEx->Out.cbPreBufThreshold + _8K, _4K),
    1068                                                   pStreamEx->Out.cbPreBufAlloc);
    1069             pStreamEx->Out.pbPreBuf = (uint8_t *)RTMemAllocZ(pStreamEx->Out.cbPreBufAlloc);
     1138            pStreamEx->Out.cbPreBufAlloc     = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props,
     1139                                                                          CfgHostAcq.Backend.cFramesBufferSize - 2);
     1140            pStreamEx->Out.cbPreBufAlloc     = RT_MIN(RT_ALIGN_32(pStreamEx->Out.cbPreBufThreshold + _8K, _4K),
     1141                                                     pStreamEx->Out.cbPreBufAlloc);
     1142            pStreamEx->Out.pbPreBuf          = (uint8_t *)RTMemAllocZ(pStreamEx->Out.cbPreBufAlloc);
    10701143            AssertReturn(pStreamEx->Out.pbPreBuf, VERR_NO_MEMORY);
    10711144        }
     1145        pStreamEx->Out.enmPlayState          = DRVAUDIOPLAYSTATE_NOPLAY; /* Changed upon enable. */
    10721146    }
    10731147
     
    13741448        pStreamEx->Out.cbPreBufAlloc = 0;
    13751449        pStreamEx->Out.cbPreBuffered = 0;
     1450        pStreamEx->Out.offPreBuf     = 0;
    13761451    }
    13771452
     
    14771552 * drvAudioStreamReInitInternal().
    14781553 *
    1479  * @param   pThis       Pointer to driver instance.
    14801554 * @param   pStreamEx   Stream to drop data for.
    14811555 */
    1482 static void drvAudioStreamDropInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
    1483 {
    1484     RT_NOREF(pThis);
    1485 
     1556static void drvAudioStreamDropInternal(PDRVAUDIOSTREAM pStreamEx)
     1557{
    14861558    LogFunc(("[%s]\n", pStreamEx->Core.szName));
    14871559
     
    14921564    }
    14931565
    1494     pStreamEx->fThresholdReached    = false;
    14951566    pStreamEx->nsLastIterated       = 0;
    14961567    pStreamEx->nsLastPlayedCaptured = 0;
    14971568    pStreamEx->nsLastReadWritten    = 0;
     1569    if (pStreamEx->Host.Cfg.enmDir == PDMAUDIODIR_OUT)
     1570    {
     1571        pStreamEx->Out.cbPreBuffered = 0;
     1572        pStreamEx->Out.offPreBuf     = 0;
     1573        pStreamEx->Out.enmPlayState  = pStreamEx->Out.cbPreBufThreshold > 0
     1574                                      ? DRVAUDIOPLAYSTATE_PREBUF : DRVAUDIOPLAYSTATE_PLAY;
     1575    }
    14981576}
    14991577
     
    15241602    const bool fIsEnabled = RT_BOOL(pStreamEx->Core.fStatus & PDMAUDIOSTREAM_STS_ENABLED); /* Stream is enabled? */
    15251603
     1604/** @todo r=bird: this is retried a bit too indiscriminately for my taste ... */
    15261605    /*
    15271606     * Destroy and re-create stream on backend side.
     
    15471626
    15481627    /* Drop all old data. */
    1549     drvAudioStreamDropInternal(pThis, pStreamEx);
     1628    drvAudioStreamDropInternal(pStreamEx);
    15501629
    15511630    /*
     
    15861665        /* Throttle re-initializing streams on failure. */
    15871666        if (   pStreamEx->cTriesReInit < cMaxTries
    1588             && tsNowNs - pStreamEx->nsLastReInit >= RT_NS_1SEC * pStreamEx->cTriesReInit) /** @todo Ditto. */
     1667            && (   pStreamEx->nsLastReInit == 0
     1668                || tsNowNs - pStreamEx->nsLastReInit >= RT_NS_1SEC * pStreamEx->cTriesReInit)) /** @todo Ditto. */
    15891669        {
    15901670#ifdef VBOX_WITH_AUDIO_ENUM
     1671/** @todo do this elsewhere.  */
    15911672            if (pThis->fEnumerateDevices)
    15921673            {
     
    16231704            /* Did we exceed our tries re-initializing the stream?
    16241705             * Then this one is dead-in-the-water, so disable it for further use. */
     1706/** @todo r=bird: This should be done above when drvAudioStreamReInitInternal fails! Duh^2! */
    16251707            if (pStreamEx->cTriesReInit == cMaxTries)
    16261708            {
     
    17981880 * Resets the given audio stream.
    17991881 *
    1800  * @param   pThis       Pointer to driver instance.
    18011882 * @param   pStreamEx   Stream to reset.
    18021883 */
    1803 static void drvAudioStreamResetInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
    1804 {
    1805     drvAudioStreamDropInternal(pThis, pStreamEx);
     1884static void drvAudioStreamResetInternal(PDRVAUDIOSTREAM pStreamEx)
     1885{
     1886    drvAudioStreamDropInternal(pStreamEx);
    18061887
    18071888    LogFunc(("[%s]\n", pStreamEx->Core.szName));
     
    19031984                if (pStreamEx->Core.fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)
    19041985                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    1905 
    1906                 if (RT_SUCCESS(rc))
    1907                     rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
    1908 
    19091986                if (RT_SUCCESS(rc))
    19101987                {
    1911                     pStreamEx->Core.fStatus |= PDMAUDIOSTREAM_STS_ENABLED;
    1912                     PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->Core.fStatus);
     1988                    /* Reset the play state before we try to start. */
     1989                    pStreamEx->fLastBackendStatus = drvAudioStreamGetBackendStatus(pThis, pStreamEx);
     1990                    if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT)
     1991                    {
     1992                        pStreamEx->Out.cbPreBuffered = 0;
     1993                        pStreamEx->Out.offPreBuf     = 0;
     1994                        pStreamEx->Out.enmPlayState  = pStreamEx->Out.cbPreBufThreshold > 0
     1995                                                     ? DRVAUDIOPLAYSTATE_PREBUF
     1996                                                     : pStreamEx->fLastBackendStatus & PDMAUDIOSTREAM_STS_INITIALIZED
     1997                                                     ? DRVAUDIOPLAYSTATE_PLAY
     1998                                                     : DRVAUDIOPLAYSTATE_NOPLAY;
     1999                        LogFunc(("ENABLE: fLastBackendStatus=%#x enmPlayState=%s\n",
     2000                                 pStreamEx->fLastBackendStatus, drvAudioPlayStateName(pStreamEx->Out.enmPlayState)));
     2001                    }
     2002                    else
     2003                        LogFunc(("ENABLE: fLastBackendStatus=%#x\n", pStreamEx->fLastBackendStatus));
     2004
     2005                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
     2006                    if (RT_SUCCESS(rc))
     2007                    {
     2008                        pStreamEx->Core.fStatus |= PDMAUDIOSTREAM_STS_ENABLED;
     2009                        PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->Core.fStatus);
     2010                    }
    19132011                }
    19142012            }
     
    19492047                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    19502048                    if (RT_SUCCESS(rc))
    1951                         drvAudioStreamResetInternal(pThis, pStreamEx);
     2049                        drvAudioStreamResetInternal(pStreamEx);
    19522050                }
    19532051            }
     
    20252123
    20262124/**
     2125 * Copy data to the pre-buffer, ring-buffer style.
     2126 *
     2127 * This is used in two slightly different situations:
     2128 *
     2129 *      -# When the stream is started (enabled) and we only want to prebuffer up
     2130 *         to the threshold before pushing the data to the backend.  We
     2131 *         typically use the max buffer size for this situation.
     2132 *
     2133 *      -# When the backend sets the PDMAUDIOSTREAM_STS_PREPARING_SWITCH
     2134 *         status bit and we're preparing for a smooth switch over to a
     2135 *         different audio device.  Most of the pre-buffered data should not be
     2136 *         played on the old device prior to the switch, due to the prebuffering
     2137 *         at the start of the stream.  We only use the threshold size for this
     2138 *         case.
     2139 */
     2140static int drvAudioStreamPreBuffer(PDRVAUDIOSTREAM pStreamEx, const uint8_t *pbBuf, uint32_t cbBuf, uint32_t cbMax)
     2141{
     2142    uint32_t const cbAlloc = pStreamEx->Out.cbPreBufAlloc;
     2143    AssertReturn(cbAlloc >= cbMax, VERR_INTERNAL_ERROR_3);
     2144    AssertReturn(cbAlloc >= 8, VERR_INTERNAL_ERROR_4);
     2145    AssertReturn(cbMax >= 8, VERR_INTERNAL_ERROR_5);
     2146
     2147    uint32_t offRead = pStreamEx->Out.offPreBuf;
     2148    uint32_t cbCur   = pStreamEx->Out.cbPreBuffered;
     2149    AssertStmt(offRead < cbAlloc, offRead %= cbAlloc);
     2150    AssertStmt(cbCur <= cbMax, offRead = (offRead + cbCur - cbMax) % cbAlloc; cbCur = cbMax);
     2151
     2152    /*
     2153     * First chunk.
     2154     */
     2155    uint32_t offWrite = (offRead + cbCur) % cbAlloc;
     2156    uint32_t cbToCopy = RT_MIN(cbAlloc - offWrite, cbBuf);
     2157    memcpy(&pStreamEx->Out.pbPreBuf[offWrite], pbBuf, cbToCopy);
     2158
     2159    /* Advance. */
     2160    offWrite = (offWrite + cbToCopy) % cbAlloc;
     2161    for (;;)
     2162    {
     2163        pbBuf    += cbToCopy;
     2164        cbCur    += cbToCopy;
     2165        if (cbCur > cbMax)
     2166            offRead = (offRead + cbCur - cbMax) % cbAlloc;
     2167        cbBuf    -= cbToCopy;
     2168        if (!cbBuf)
     2169            break;
     2170
     2171        /*
     2172         * Second+ chunk, from the start of the buffer.
     2173         *
     2174         * Note! It is assumed very unlikely that we will ever see a cbBuf larger than
     2175         *       cbMax, so we don't waste space on clipping cbBuf here (can happen with
     2176         *       custom pre-buffer sizes).
     2177         */
     2178        Assert(offWrite == 0);
     2179        cbToCopy = RT_MIN(cbAlloc, cbBuf);
     2180        memcpy(pStreamEx->Out.pbPreBuf, pbBuf, cbToCopy);
     2181    }
     2182
     2183    /*
     2184     * Update the pre-buffering size and position.
     2185     */
     2186    pStreamEx->Out.cbPreBuffered = cbCur;
     2187    pStreamEx->Out.offPreBuf     = offRead;
     2188    return VINF_SUCCESS;
     2189}
     2190
     2191
     2192#if 0
     2193/**
    20272194 * Worker for drvAudioStreamPlay() and drvAudioStreamIterateInternal().
    20282195 *
     
    20662233            cbWritten = RT_MIN(cbFree, cbBuf);
    20672234            cbWritten = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Props, cbWritten);
    2068             memcpy(&pStreamEx->Out.pbPreBuf[pStreamEx->Out.cbPreBuffered], pbBuf, cbWritten);
     2235            if (pStreamEx->Out.offPreBuf == 0)
     2236                memcpy(&pStreamEx->Out.pbPreBuf[pStreamEx->Out.cbPreBuffered], pbBuf, cbWritten);
     2237            else
     2238            {
     2239
     2240            }
     2241
    20692242            pStreamEx->Out.cbPreBuffered += cbWritten;
    20702243            cbBuf                        -= cbWritten;
     
    22062379    return rc;
    22072380}
     2381#endif
     2382
     2383
     2384/**
     2385 * Worker for drvAudioStreamPlay() and drvAudioStreamIterateInternal().
     2386 *
     2387 * Caller owns the lock.
     2388 */
     2389static int drvAudioStreamPlayLocked(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
     2390                                    const uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbWritten)
     2391{
     2392    Log3Func(("%s: @%#RX64: cbBuf=%#x\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbBuf));
     2393
     2394    uint32_t      cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     2395    pStreamEx->Out.Stats.cbBackendWritableBefore = cbWritable;
     2396
     2397    uint32_t      cbWritten  = 0;
     2398    int           rc         = VINF_SUCCESS;
     2399    uint8_t const cbFrame    = PDMAudioPropsFrameSize(&pStreamEx->Core.Props);
     2400    while (cbBuf >= cbFrame && cbWritable >= cbFrame)
     2401    {
     2402        uint32_t const cbToWrite    = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Props, RT_MIN(cbBuf, cbWritable));
     2403        uint32_t       cbWrittenNow = 0;
     2404        rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, pbBuf, cbToWrite, &cbWrittenNow);
     2405        if (RT_SUCCESS(rc))
     2406        {
     2407            if (cbWrittenNow != cbToWrite)
     2408                Log3Func(("%s: @%#RX64: Wrote less bytes than requested: %#x, requested %#x\n",
     2409                          pStreamEx->Core.szName, pStreamEx->offInternal, cbWrittenNow, cbToWrite));
     2410#ifdef DEBUG_bird
     2411            Assert(cbWrittenNow == cbToWrite);
     2412#endif
     2413            AssertStmt(cbWrittenNow <= cbToWrite, cbWrittenNow = cbToWrite);
     2414            cbWritten              += cbWrittenNow;
     2415            cbBuf                  -= cbWrittenNow;
     2416            pbBuf                  += cbWrittenNow;
     2417            pStreamEx->offInternal += cbWrittenNow;
     2418        }
     2419        else
     2420        {
     2421            *pcbWritten = cbWritten;
     2422            LogFunc(("%s: @%#RX64: pfnStreamPlay failed writing %#x bytes (%#x previous written, %#x writable): %Rrc\n",
     2423                     pStreamEx->Core.szName, pStreamEx->offInternal, cbToWrite, cbWritten, cbWritable, rc));
     2424            return cbWritten ? VINF_SUCCESS : rc;
     2425        }
     2426
     2427        cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     2428    }
     2429
     2430    *pcbWritten = cbWritten;
     2431    pStreamEx->Out.Stats.cbBackendWritableAfter = cbWritable;
     2432    if (cbWritten)
     2433        pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();
     2434
     2435    Log3Func(("%s: @%#RX64: Wrote %#x bytes (%#x bytes left)\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbWritten, cbBuf));
     2436    return rc;
     2437}
     2438
     2439
     2440static int drvAudioStreamPlayToPreBuffer(PDRVAUDIOSTREAM pStreamEx, const void *pvBuf, uint32_t cbBuf, uint32_t cbMax,
     2441                                         uint32_t *pcbWritten)
     2442{
     2443    int rc = drvAudioStreamPreBuffer(pStreamEx, (uint8_t const *)pvBuf, cbBuf, cbMax);
     2444    if (RT_SUCCESS(rc))
     2445    {
     2446        *pcbWritten = cbBuf;
     2447        pStreamEx->offInternal += cbBuf;
     2448        Log3Func(("[%s] Pre-buffering (%s): wrote %#x bytes => %#x bytes / %u%%\n",
     2449                  pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState), cbBuf, pStreamEx->Out.cbPreBuffered,
     2450                  pStreamEx->Out.cbPreBuffered * 100 / RT_MAX(pStreamEx->Out.cbPreBufThreshold, 1)));
     2451
     2452    }
     2453    else
     2454        *pcbWritten = 0;
     2455    return rc;
     2456}
     2457
     2458
     2459/**
     2460 * Used when we're committing (transfering) the pre-buffered bytes to the
     2461 * device.
     2462 *
     2463 * This is called both from drvAudioStreamPlay() and
     2464 * drvAudioStreamIterateInternal().
     2465 *
     2466 * @returns VBox status code.
     2467 * @param   pThis       Pointer to the DrvAudio instance data.
     2468 * @param   pStreamEx   The stream to commit the pre-buffering for.
     2469 * @param   pbBuf       Buffer with new bytes to write.  Can be NULL when called
     2470 *                      in the PENDING_DISABLE state from
     2471 *                      drvAudioStreamIterateInternal().
     2472 * @param   cbBuf       Number of new bytes.  Can be zero.
     2473 * @param   pcbWritten  Where to return the number of bytes written.
     2474 */
     2475static int drvAudioStreamPreBufComitting(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
     2476                                         const uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbWritten)
     2477{
     2478    /*
     2479     * First, top up the buffer with new data from pbBuf.
     2480     */
     2481    *pcbWritten = 0;
     2482    if (cbBuf > 0)
     2483    {
     2484        uint32_t const cbToCopy = RT_MIN(pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered, cbBuf);
     2485        if (cbToCopy > 0)
     2486        {
     2487            int rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pbBuf, cbBuf, pStreamEx->Out.cbPreBufAlloc, pcbWritten);
     2488            AssertRCReturn(rc, rc);
     2489            pbBuf += cbToCopy;
     2490            cbBuf -= cbToCopy;
     2491        }
     2492    }
     2493
     2494    /*
     2495     * Write the pre-buffered chunk.
     2496     */
     2497    int             rc      = VINF_SUCCESS;
     2498    uint32_t const  cbAlloc = pStreamEx->Out.cbPreBufAlloc;
     2499    AssertReturn(cbAlloc > 0, VERR_INTERNAL_ERROR_2);
     2500    uint32_t        off     = pStreamEx->Out.offPreBuf;
     2501    AssertStmt(off < pStreamEx->Out.cbPreBufAlloc, off %= cbAlloc);
     2502    uint32_t        cbLeft  = pStreamEx->Out.cbPreBuffered;
     2503    while (cbLeft > 0)
     2504    {
     2505        uint32_t const cbToWrite = RT_MIN(cbAlloc - off, cbLeft);
     2506        Assert(cbToWrite > 0);
     2507
     2508        uint32_t cbPreBufWritten = 0;
     2509        rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, &pStreamEx->Out.pbPreBuf[off],
     2510                                                 cbToWrite, &cbPreBufWritten);
     2511        AssertRCBreak(rc);
     2512        if (!cbPreBufWritten)
     2513            break;
     2514        AssertStmt(cbPreBufWritten <= cbToWrite, cbPreBufWritten = cbToWrite);
     2515        off     = (off + cbPreBufWritten) % cbAlloc;
     2516        cbLeft -= cbPreBufWritten;
     2517    }
     2518
     2519    if (cbLeft == 0)
     2520    {
     2521        LogFunc(("@%#RX64: Wrote all %#x bytes of pre-buffered audio data. %s -> PLAY\n", pStreamEx->offInternal,
     2522                 pStreamEx->Out.cbPreBuffered, drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
     2523        pStreamEx->Out.cbPreBuffered = 0;
     2524        pStreamEx->Out.offPreBuf     = 0;
     2525        pStreamEx->Out.enmPlayState  = DRVAUDIOPLAYSTATE_PLAY;
     2526
     2527        if (cbBuf > 0)
     2528        {
     2529            uint32_t cbWritten2 = 0;
     2530            rc = drvAudioStreamPlayLocked(pThis, pStreamEx, pbBuf, cbBuf, &cbWritten2);
     2531            if (RT_SUCCESS(rc))
     2532                *pcbWritten += cbWritten2;
     2533        }
     2534        else
     2535            pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();
     2536    }
     2537    else
     2538    {
     2539        if (cbLeft != pStreamEx->Out.cbPreBuffered)
     2540            pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();
     2541
     2542        LogRel2(("Audio: @%#RX64: Stream '%s' pre-buffering commit problem: wrote %#x out of %#x + %#x - rc=%Rrc *pcbWritten=%#x %s -> PREBUF_COMMITTING\n",
     2543                 pStreamEx->offInternal, pStreamEx->Core.szName, pStreamEx->Out.cbPreBuffered - cbLeft,
     2544                 pStreamEx->Out.cbPreBuffered, cbBuf, rc, *pcbWritten, drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
     2545        AssertMsg(pStreamEx->Out.enmPlayState == DRVAUDIOPLAYSTATE_PREBUF_COMMITTING || RT_FAILURE(rc),
     2546                  ("Buggy host driver buffer reporting? cbLeft=%#x cbPreBuffered=%#x\n", cbLeft, pStreamEx->Out.cbPreBuffered));
     2547
     2548        pStreamEx->Out.cbPreBuffered = cbLeft;
     2549        pStreamEx->Out.offPreBuf     = off;
     2550        pStreamEx->Out.enmPlayState  = DRVAUDIOPLAYSTATE_PREBUF_COMMITTING;
     2551    }
     2552
     2553    return *pcbWritten ? VINF_SUCCESS : rc;
     2554}
    22082555
    22092556
     
    22602607         *        is played.  That means that this code and associated timer hack
    22612608         *        should probably not be here at all. */
    2262         uint32_t cFramesLive;
    2263         cFramesLive = pStreamEx->Out.cbPreBuffered;
    2264         if (cFramesLive > 0)
    2265         {
    2266             uint32_t cbIgnored = 0;
    2267             drvAudioStreamPlayLocked(pThis, pStreamEx,
    2268                                      pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStreamEx->pBackend),
    2269                                      NULL, 0, &cbIgnored);
    2270             cFramesLive = PDMAudioPropsBytesToFrames(&pStreamEx->Core.Props, pStreamEx->Out.cbPreBuffered);
     2609        uint32_t cFramesLive = 0;
     2610        switch (pStreamEx->Out.enmPlayState)
     2611        {
     2612            case DRVAUDIOPLAYSTATE_PLAY:                /* Nothing prebuffered. */
     2613            case DRVAUDIOPLAYSTATE_PLAY_PREBUF:         /* Not pre-buffering for current device. */
     2614            case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:      /* Output device isn't ready, drop it. */ /** @todo check state? */
     2615            case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:    /* No output device yet. */
     2616            case DRVAUDIOPLAYSTATE_NOPLAY:
     2617            case DRVAUDIOPLAYSTATE_INVALID:
     2618            case DRVAUDIOPLAYSTATE_END:
     2619                /* no default, want warnings. */
     2620                break;
     2621            case DRVAUDIOPLAYSTATE_PREBUF:
     2622            case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
     2623                if (pStreamEx->Out.cbPreBuffered > 0)
     2624                {
     2625                    uint32_t cbIgnored = 0;
     2626                    drvAudioStreamPreBufComitting(pThis, pStreamEx, NULL, 0, &cbIgnored);
     2627                    cFramesLive = PDMAudioPropsBytesToFrames(&pStreamEx->Core.Props, pStreamEx->Out.cbPreBuffered);
     2628                }
     2629                break;
    22712630        }
    22722631        Log3Func(("[%s] cFramesLive=%RU32\n", pStreamEx->Core.szName, cFramesLive));
     
    23052664                        pStreamEx->Core.fStatus &= ~(PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PENDING_DISABLE);
    23062665                        PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->Core.fStatus);
    2307                         drvAudioStreamDropInternal(pThis, pStreamEx); /* Not a DROP command, just a stream reset. */
     2666                        drvAudioStreamDropInternal(pStreamEx); /* Not a DROP command, just a stream reset. */
    23082667                    }
    23092668                    else
     
    23972756             * Reading the actual data from a stream then will return silence then.
    23982757             */
    2399             uint32_t fStatus = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStreamEx->pBackend);
    2400             if (   !PDMAudioStrmStatusCanRead(fStatus)
     2758            uint32_t fStatus = drvAudioStreamGetBackendStatus(pThis, pStreamEx);
     2759            if (   !PDMAudioStrmStatusBackendCanRead(fStatus)
    24012760                || fDisabled)
    24022761            {
     
    24452804    /*
    24462805     * ...
     2806     *
     2807     * Note: We don't propagate the backend stream's status to the outside -- it's the job of this
     2808     *       audio connector to make sense of it.
    24472809     */
    24482810    uint32_t cbWritable = 0;
    2449 
    2450     /* Note: We don't propagate the backend stream's status to the outside -- it's the job of this
    2451      *       audio connector to make sense of it. */
    2452     if (PDMAudioStrmStatusCanWrite(pStreamEx->Core.fStatus))
    2453     {
    2454         if (pStreamEx->fNoMixBufs)
    2455         {
    2456             Assert(pThis->pHostDrvAudio);
    2457             cbWritable = pThis->pHostDrvAudio
    2458                        ? pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend) : 0;
    2459             if (pStreamEx->fThresholdReached)
     2811    if (   PDMAudioStrmStatusCanWrite(pStreamEx->Core.fStatus)
     2812        && pThis->pHostDrvAudio != NULL)
     2813    {
     2814        Assert(pThis->pHostDrvAudio);
     2815        switch (pStreamEx->Out.enmPlayState)
     2816        {
     2817            /*
     2818             * Whatever the backend can hold.
     2819             */
     2820            case DRVAUDIOPLAYSTATE_PLAY:
     2821            case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
     2822                cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     2823                break;
     2824
     2825            /*
     2826             * Whatever we've got of available space in the pre-buffer.
     2827             * Note! For the last round when we pass the pre-buffering threshold, we may
     2828             *       report fewer bytes than what a DMA timer period for the guest device
     2829             *       typically produces, however that should be transfered in the following
     2830             *       round that goes directly to the backend buffer.
     2831             */
     2832            case DRVAUDIOPLAYSTATE_PREBUF:
     2833                cbWritable = pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered;
     2834                if (!cbWritable)
     2835                    cbWritable = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Props, 2);
     2836                break;
     2837
     2838            /*
     2839             * These are slightly more problematic and can go wrong if the pre-buffer is
     2840             * manually configured to be smaller than the output of a typeical DMA timer
     2841             * period for the guest device.  So, to overcompensate, we just report back
     2842             * the backend buffer size (the pre-buffer is circular, so no overflow issue).
     2843             */
     2844            case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
     2845            case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
     2846                cbWritable = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Props,
     2847                                                        RT_MAX(pStreamEx->Host.Cfg.Backend.cFramesBufferSize,
     2848                                                               pStreamEx->Host.Cfg.Backend.cFramesPreBuffering));
     2849                break;
     2850
     2851            case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
    24602852            {
    2461                 if (pStreamEx->Out.cbPreBuffered == 0)
    2462                 { /* likely */ }
     2853                /* Buggy backend: We weren't able to copy all the pre-buffered data to it
     2854                   when reaching the threshold.  Try escape this situation, or at least
     2855                   keep the extra buffering to a minimum.  We must try write something
     2856                   as long as there is space for it, as we need the pfnStreamWrite call
     2857                   to move the data. */
     2858                uint32_t const cbMin = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Props, 8);
     2859                cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     2860                if (cbWritable >= pStreamEx->Out.cbPreBuffered + cbMin)
     2861                    cbWritable -= pStreamEx->Out.cbPreBuffered + cbMin / 2;
    24632862                else
    2464                 {
    2465                     /* Buggy backend: We weren't able to copy all the pre-buffered data to it
    2466                        when reaching the threshold.  Try escape this situation, or at least
    2467                        keep the extra buffering to a minimum.  We must try write something
    2468                        as long as there is space for it, as we need the pfnStreamWrite call
    2469                        to move the data. */
    2470                     uint32_t const cbMin = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Props, 8);
    2471                     if (cbWritable >= pStreamEx->Out.cbPreBuffered + cbMin)
    2472                         cbWritable -= pStreamEx->Out.cbPreBuffered + cbMin / 2;
    2473                     else
    2474                         cbWritable = RT_MIN(cbMin, pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered);
    2475                     AssertLogRel(cbWritable);
    2476                 }
     2863                    cbWritable = RT_MIN(cbMin, pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered);
     2864                AssertLogRel(cbWritable);
     2865                break;
    24772866            }
    2478             else
    2479             {
    2480                 Assert(cbWritable >= pStreamEx->Out.cbPreBufThreshold);
    2481                 cbWritable = pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBufThreshold;
    2482             }
    2483         }
    2484         else
    2485             cbWritable = AudioMixBufFreeBytes(&pStreamEx->Host.MixBuf);
     2867
     2868            case DRVAUDIOPLAYSTATE_NOPLAY:
     2869                break;
     2870            case DRVAUDIOPLAYSTATE_INVALID:
     2871            case DRVAUDIOPLAYSTATE_END:
     2872                AssertFailed();
     2873                break;
     2874        }
    24862875
    24872876        /* Make sure to align the writable size to the host's frame size. */
     
    25462935
    25472936    return VINF_SUCCESS;
     2937}
     2938
     2939
     2940static void drvAudioStreamPlayProcessBackendStateChange(PDRVAUDIOSTREAM pStreamEx, uint32_t fNewState, uint32_t fOldState)
     2941{
     2942    DRVAUDIOPLAYSTATE const enmPlayState = pStreamEx->Out.enmPlayState;
     2943
     2944    /*
     2945     * Did PDMAUDIOSTREAM_STS_INITIALIZED change?
     2946     */
     2947    if ((fOldState ^ fNewState) & PDMAUDIOSTREAM_STS_INITIALIZED)
     2948    {
     2949        if (fOldState & PDMAUDIOSTREAM_STS_INITIALIZED)
     2950        {
     2951            switch (enmPlayState)
     2952            {
     2953                case DRVAUDIOPLAYSTATE_PLAY:
     2954                case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
     2955                    pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
     2956                    /** @todo We could enter PREBUF here and hope for the device to re-appear in
     2957                     *        initialized state... */
     2958                    break;
     2959                case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
     2960                    pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_SWITCHING;
     2961                    break;
     2962                case DRVAUDIOPLAYSTATE_PREBUF:
     2963                    break;
     2964                case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
     2965                case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
     2966                    AssertFailedBreak();
     2967                /* no default */
     2968                case DRVAUDIOPLAYSTATE_NOPLAY:
     2969                case DRVAUDIOPLAYSTATE_END:
     2970                case DRVAUDIOPLAYSTATE_INVALID:
     2971                    break;
     2972            }
     2973            LogFunc(("PDMAUDIOSTREAM_STS_INITIALIZED was cleared: %s -> %s\n",
     2974                     drvAudioPlayStateName(enmPlayState), drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
     2975        }
     2976        else
     2977        {
     2978            switch (enmPlayState)
     2979            {
     2980                case DRVAUDIOPLAYSTATE_PREBUF:
     2981                case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
     2982                    break;
     2983                case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
     2984                    pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_COMMITTING;
     2985                    break;
     2986                case DRVAUDIOPLAYSTATE_NOPLAY:
     2987                    pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF;
     2988                    break;
     2989                case DRVAUDIOPLAYSTATE_PLAY:
     2990                case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
     2991                case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
     2992                    AssertFailedBreak();
     2993                /* no default */
     2994                case DRVAUDIOPLAYSTATE_END:
     2995                case DRVAUDIOPLAYSTATE_INVALID:
     2996                    break;
     2997            }
     2998            LogFunc(("PDMAUDIOSTREAM_STS_INITIALIZED was set: %s -> %s\n",
     2999                     drvAudioPlayStateName(enmPlayState), drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
     3000        }
     3001    }
     3002
     3003    /*
     3004     * Deal with PDMAUDIOSTREAM_STS_PREPARING_SWITCH being set.
     3005     *
     3006     * Note! We don't care if it's cleared as the backend will call
     3007     *       PDMIAUDIONOTIFYFROMHOST::pfnStreamNotifyDeviceChanged when that takes place.
     3008     */
     3009    if (   !(fOldState & PDMAUDIOSTREAM_STS_PREPARING_SWITCH)
     3010        && (fNewState  & PDMAUDIOSTREAM_STS_PREPARING_SWITCH))
     3011    {
     3012        if (pStreamEx->Out.cbPreBufThreshold > 0)
     3013        {
     3014            switch (enmPlayState)
     3015            {
     3016                case DRVAUDIOPLAYSTATE_PREBUF:
     3017                case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
     3018                case DRVAUDIOPLAYSTATE_NOPLAY:
     3019                case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING: /* simpler */
     3020                    pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_SWITCHING;
     3021                    break;
     3022                case DRVAUDIOPLAYSTATE_PLAY:
     3023                    pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PLAY_PREBUF;
     3024                    break;
     3025                case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
     3026                case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
     3027                    break;
     3028                /* no default */
     3029                case DRVAUDIOPLAYSTATE_END:
     3030                case DRVAUDIOPLAYSTATE_INVALID:
     3031                    break;
     3032            }
     3033            LogFunc(("PDMAUDIOSTREAM_STS_INITIALIZED was set: %s -> %s\n",
     3034                     drvAudioPlayStateName(enmPlayState), drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
     3035        }
     3036        else
     3037            LogFunc(("PDMAUDIOSTREAM_STS_PREPARING_SWITCH was set, but no pre-buffering configured.\n"));
     3038    }
    25483039}
    25493040
     
    25903081    if (PDMAudioStrmStatusIsReady(pStreamEx->Core.fStatus))
    25913082    {
    2592         uint32_t fBackStatus;
    25933083        if (   pThis->Out.fEnabled /* (see @bugref{9882}) */
    2594             && pThis->pHostDrvAudio != NULL
    2595             && PDMAudioStrmStatusCanWrite((fBackStatus = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio,
    2596                                                                                                   pStreamEx->pBackend))))
    2597         {
    2598             uint64_t offInternalBefore = pStreamEx->offInternal; RT_NOREF(offInternalBefore);
    2599             rc = drvAudioStreamPlayLocked(pThis, pStreamEx, fBackStatus, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
    2600             Assert(offInternalBefore + *pcbWritten == pStreamEx->offInternal);
     3084            && pThis->pHostDrvAudio != NULL)
     3085        {
     3086#ifdef LOG_ENABLED
     3087            char szState[DRVAUDIO_STATUS_STR_MAX];
     3088#endif
     3089            /*
     3090             * Get the backend state and check if we've changed to "initialized" or
     3091             * to switch prep mode.
     3092             *
     3093             * We don't really need to watch ENABLE or PAUSE here as these should be
     3094             * in sync between our state and the backend (there is a tiny tiny chance
     3095             * that we are resumed before the backend driver, but AIO threads really
     3096             * shouldn't be getting here that fast I hope).
     3097             */
     3098            /** @todo
     3099             * The PENDING_DISABLE (== draining) is not reported by most backend and it's an
     3100             * open question whether we should still allow writes even when the backend
     3101             * is draining anyway.  We currently don't.  Maybe we just re-define it as
     3102             * a non-backend status flag.
     3103             */
     3104            uint32_t const fBackendStatus = drvAudioStreamGetBackendStatus(pThis, pStreamEx);
     3105            Assert(   (fBackendStatus          & (PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PAUSED))
     3106                   == (pStreamEx->Core.fStatus & (PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PAUSED)) );
     3107
     3108            if (!(  (pStreamEx->fLastBackendStatus ^ fBackendStatus)
     3109                  & (PDMAUDIOSTREAM_STS_INITIALIZED | PDMAUDIOSTREAM_STS_PREPARING_SWITCH)))
     3110            { /* no relevant change - likely */ }
     3111            else
     3112            {
     3113                drvAudioStreamPlayProcessBackendStateChange(pStreamEx, fBackendStatus, pStreamEx->fLastBackendStatus);
     3114                pStreamEx->fLastBackendStatus = fBackendStatus;
     3115            }
     3116
     3117            /*
     3118             * Do the transfering.
     3119             */
     3120            switch (pStreamEx->Out.enmPlayState)
     3121            {
     3122                case DRVAUDIOPLAYSTATE_PLAY:
     3123                    rc = drvAudioStreamPlayLocked(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
     3124                    break;
     3125
     3126                case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
     3127                    rc = drvAudioStreamPlayLocked(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
     3128                    drvAudioStreamPreBuffer(pStreamEx, (uint8_t const *)pvBuf, *pcbWritten, pStreamEx->Out.cbPreBufThreshold);
     3129                    break;
     3130
     3131                case DRVAUDIOPLAYSTATE_PREBUF:
     3132                    if (cbBuf + pStreamEx->Out.cbPreBuffered < pStreamEx->Out.cbPreBufThreshold)
     3133                        rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->Out.cbPreBufThreshold, pcbWritten);
     3134                    else if (fBackendStatus & PDMAUDIOSTREAM_STS_INITIALIZED)
     3135                    {
     3136                        Log3Func(("[%s] Pre-buffering completing: cbBuf=%#x cbPreBuffered=%#x => %#x vs cbPreBufThreshold=%#x\n",
     3137                                  pStreamEx->Core.szName, cbBuf, pStreamEx->Out.cbPreBuffered,
     3138                                  cbBuf + pStreamEx->Out.cbPreBuffered, pStreamEx->Out.cbPreBufThreshold));
     3139                        rc = drvAudioStreamPreBufComitting(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
     3140                    }
     3141                    else
     3142                    {
     3143                        Log3Func(("[%s] Pre-buffering completing but device not ready: cbBuf=%#x cbPreBuffered=%#x => %#x vs cbPreBufThreshold=%#x; PREBUF -> PREBUF_OVERDUE\n",
     3144                                  pStreamEx->Core.szName, cbBuf, pStreamEx->Out.cbPreBuffered,
     3145                                  cbBuf + pStreamEx->Out.cbPreBuffered, pStreamEx->Out.cbPreBufThreshold));
     3146                        pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_OVERDUE;
     3147                        rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->Out.cbPreBufThreshold, pcbWritten);
     3148                    }
     3149                    break;
     3150
     3151                case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
     3152                    Assert(!(fBackendStatus & PDMAUDIOSTREAM_STS_INITIALIZED));
     3153                    RT_FALL_THRU();
     3154                case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
     3155                    rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->Out.cbPreBufThreshold, pcbWritten);
     3156                    break;
     3157
     3158                case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
     3159                    rc = drvAudioStreamPreBufComitting(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
     3160                    break;
     3161
     3162                case DRVAUDIOPLAYSTATE_NOPLAY:
     3163                    *pcbWritten = cbBuf;
     3164                    pStreamEx->offInternal += cbBuf;
     3165                    Log3Func(("[%s] Discarding the data, backend state: %s\n", pStreamEx->Core.szName,
     3166                              dbgAudioStreamStatusToStr(szState, fBackendStatus) ));
     3167                    break;
     3168
     3169                default:
     3170                    *pcbWritten = cbBuf;
     3171                    AssertMsgFailedBreak(("%d; cbBuf=%#x\n", pStreamEx->Out.enmPlayState, cbBuf));
     3172            }
     3173
    26013174            if (!pThis->Out.Cfg.Dbg.fEnabled || RT_FAILURE(rc))
    26023175            { /* likely */ }
    26033176            else
    26043177                AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pvBuf, *pcbWritten, 0 /* fFlags */);
    2605 
    26063178        }
    26073179        else
    26083180        {
     3181            *pcbWritten = cbBuf;
     3182            pStreamEx->offInternal += cbBuf;
    26093183            Log3Func(("[%s] Backend stream %s, discarding the data\n", pStreamEx->Core.szName,
    26103184                      !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet"));
    2611             *pcbWritten = cbBuf;
    2612             pStreamEx->offInternal += cbBuf;
    26133185        }
    26143186    }
     
    29863558
    29873559/**
    2988  * Schedules a re-initialization of all current audio streams.
    2989  * The actual re-initialization will happen at some later point in time.
    2990  *
    2991  * @param   pThis               Pointer to driver instance.
    2992  */
    2993 static void drvAudioScheduleReInitInternal(PDRVAUDIO pThis)
    2994 {
    2995     LogFunc(("\n"));
    2996 
    2997     /* Mark all host streams to re-initialize. */
     3560 * Marks a stream for re-init.
     3561 */
     3562static void drvAudioStreamMarkNeedReInit(PDRVAUDIOSTREAM pStreamEx, const char *pszCaller)
     3563{
     3564    LogFlow((LOG_FN_FMT ": Flagging %s for re-init.\n", pStreamEx->Core.szName)); RT_NOREF(pszCaller);
     3565    pStreamEx->Core.fStatus |= PDMAUDIOSTREAM_STS_NEED_REINIT;
     3566    PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->Core.fStatus);
     3567    pStreamEx->cTriesReInit  = 0;
     3568    pStreamEx->nsLastReInit  = 0;
     3569}
     3570
     3571
     3572/**
     3573 * @interface_method_impl{PDMIAUDIONOTIFYFROMHOST,pfnNotifyDeviceChanged}
     3574 */
     3575static DECLCALLBACK(void) drvAudioNotifyFromHost_NotifyDeviceChanged(PPDMIAUDIONOTIFYFROMHOST pInterface,
     3576                                                                     PDMAUDIODIR enmDir, void *pvUser)
     3577{
     3578    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioNotifyFromHost);
     3579    AssertReturnVoid(enmDir == PDMAUDIODIR_IN || enmDir == PDMAUDIODIR_OUT);
     3580    LogRel(("Audio: The %s device for %s is changing.\n", enmDir == PDMAUDIODIR_IN ? "input" : "output", pThis->szName));
     3581
     3582    RTCritSectEnter(&pThis->CritSect);
    29983583    PDRVAUDIOSTREAM pStreamEx;
    29993584    RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
    30003585    {
    3001         pStreamEx->Core.fStatus |= PDMAUDIOSTREAM_STS_NEED_REINIT;
    3002         PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->Core.fStatus);
    3003         pStreamEx->cTriesReInit  = 0;
    3004         pStreamEx->nsLastReInit  = 0;
     3586        if (pStreamEx->Core.enmDir == enmDir)
     3587        {
     3588            if (pThis->pHostDrvAudio->pfnStreamNotifyDeviceChanged)
     3589            {
     3590                LogFlowFunc(("Calling pfnStreamNotifyDeviceChanged on %s, old backend status: %#x...\n", pStreamEx->Core.szName,
     3591                              drvAudioStreamGetBackendStatus(pThis, pStreamEx) ));
     3592                pThis->pHostDrvAudio->pfnStreamNotifyDeviceChanged(pThis->pHostDrvAudio, pStreamEx->pBackend, pvUser);
     3593                LogFlowFunc(("New stream backend status: %#x.\n", drvAudioStreamGetBackendStatus(pThis, pStreamEx) ));
     3594            }
     3595            else
     3596                drvAudioStreamMarkNeedReInit(pStreamEx, __PRETTY_FUNCTION__);
     3597        }
     3598    }
     3599    RTCritSectLeave(&pThis->CritSect);
     3600}
     3601
     3602
     3603/**
     3604 * @interface_method_impl{PDMIAUDIONOTIFYFROMHOST,pfnStreamNotifyDeviceChanged}
     3605 */
     3606static DECLCALLBACK(void) drvAudioNotifyFromHost_StreamNotifyDeviceChanged(PPDMIAUDIONOTIFYFROMHOST pInterface,
     3607                                                                           PPDMAUDIOBACKENDSTREAM pStream, bool fReInit)
     3608{
     3609    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioNotifyFromHost);
     3610
     3611    /*
     3612     * Backend stream to validated DrvAudio stream:
     3613     */
     3614    AssertPtrReturnVoid(pStream);
     3615    AssertReturnVoid(pStream->uMagic == PDMAUDIOBACKENDSTREAM_MAGIC);
     3616    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream->pStream;
     3617    AssertPtrReturnVoid(pStreamEx);
     3618    AssertReturnVoid(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC);
     3619    AssertReturnVoid(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC);
     3620
     3621    /*
     3622     * Grab the lock and do the requested work.
     3623     */
     3624    RTCritSectEnter(&pThis->CritSect);
     3625    AssertReturnVoidStmt(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, RTCritSectLeave(&pThis->CritSect)); /* paranoia */
     3626
     3627    if (fReInit)
     3628        drvAudioStreamMarkNeedReInit(pStreamEx, __PRETTY_FUNCTION__);
     3629    else
     3630    {
     3631        /*
     3632         * Adjust the stream state now that the device has (perhaps finally) been switched.
     3633         *
     3634         * For enabled output streams, we must update the play state.  We could try commit
     3635         * pre-buffered data here, but it's really not worth the hazzle and risk (don't
     3636         * know which thread we're on, do we now).
     3637         */
     3638        AssertStmt(!(pStreamEx->Core.fStatus & PDMAUDIOSTREAM_STS_NEED_REINIT),
     3639                   pStreamEx->Core.fStatus &= ~PDMAUDIOSTREAM_STS_NEED_REINIT);
     3640
     3641        if (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
     3642            && (pStreamEx->Core.fStatus & PDMAUDIOSTREAM_STS_ENABLED))
     3643        {
     3644            DRVAUDIOPLAYSTATE const enmPlayState = pStreamEx->Out.enmPlayState;
     3645            pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF;
     3646            LogFunc(("%s: %s -> %s\n", pStreamEx->Core.szName,
     3647                     drvAudioPlayStateName(enmPlayState), drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
     3648        }
     3649    }
     3650
     3651    RTCritSectLeave(&pThis->CritSect);
     3652}
     3653
     3654
     3655/**
     3656 * @interface_method_impl{PDMIAUDIONOTIFYFROMHOST,pfnNotifyDevicesChanged}
     3657 */
     3658static DECLCALLBACK(void) drvAudioNotifyFromHost_NotifyDevicesChanged(PPDMIAUDIONOTIFYFROMHOST pInterface)
     3659{
     3660    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioNotifyFromHost);
     3661    LogRel(("Audio: Device configuration of driver '%s' has changed\n", pThis->szName));
     3662
     3663    /** @todo Remove legacy behaviour: */
     3664    if (!pThis->pHostDrvAudio->pfnStreamNotifyDeviceChanged)
     3665    {
     3666/** @todo r=bird: Locking?   */
     3667        /* Mark all host streams to re-initialize. */
     3668        PDRVAUDIOSTREAM pStreamEx;
     3669        RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
     3670        {
     3671            drvAudioStreamMarkNeedReInit(pStreamEx, __PRETTY_FUNCTION__);
     3672        }
    30053673    }
    30063674
     
    30093677    pThis->fEnumerateDevices = true;
    30103678# endif
    3011 }
    3012 
    3013 
    3014 /**
    3015  * @interface_method_impl{PDMIAUDIONOTIFYFROMHOST,pfnNotifyDevicesChanged}
    3016  */
    3017 static DECLCALLBACK(void) drvAudioNotifyFromHost_NotifyDevicesChanged(PPDMIAUDIONOTIFYFROMHOST pInterface)
    3018 {
    3019     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioNotifyFromHost);
    3020 
    3021     LogRel(("Audio: Device configuration of driver '%s' has changed\n", pThis->szName));
    3022     drvAudioScheduleReInitInternal(pThis);
    30233679}
    30243680
     
    35714227    pThis->IAudioConnector.pfnStreamCapture     = drvAudioStreamCapture;
    35724228    /* IAudioNotifyFromHost */
    3573     pThis->IAudioNotifyFromHost.pfnNotifyDevicesChanged = drvAudioNotifyFromHost_NotifyDevicesChanged;
     4229    pThis->IAudioNotifyFromHost.pfnNotifyDeviceChanged          = drvAudioNotifyFromHost_NotifyDeviceChanged;
     4230    pThis->IAudioNotifyFromHost.pfnStreamNotifyDeviceChanged    = drvAudioNotifyFromHost_StreamNotifyDeviceChanged;
     4231    pThis->IAudioNotifyFromHost.pfnNotifyDevicesChanged         = drvAudioNotifyFromHost_NotifyDevicesChanged;
    35744232
    35754233    /*
  • trunk/src/VBox/Devices/Audio/DrvHostAudioNull.cpp

    r88724 r88760  
    229229DECL_HIDDEN_CONST(PDMIHOSTAUDIO) const g_DrvHostAudioNull =
    230230{
    231     /* .pfnGetConfig          =*/ drvHostNullAudioHA_GetConfig,
    232     /* .pfnGetDevices         =*/ NULL,
    233     /* .pfnGetStatus          =*/ drvHostNullAudioHA_GetStatus,
    234     /* .pfnStreamConfigHint   =*/ NULL,
    235     /* .pfnStreamCreate       =*/ drvHostNullAudioHA_StreamCreate,
    236     /* .pfnStreamDestroy      =*/ drvHostNullAudioHA_StreamDestroy,
    237     /* .pfnStreamControl      =*/ drvHostNullAudioHA_StreamControl,
    238     /* .pfnStreamGetReadable  =*/ drvHostNullAudioHA_StreamGetReadable,
    239     /* .pfnStreamGetWritable  =*/ drvHostNullAudioHA_StreamGetWritable,
    240     /* .pfnStreamGetPending   =*/ drvHostNullAudioHA_StreamGetPending,
    241     /* .pfnStreamGetStatus    =*/ drvHostNullAudioHA_StreamGetStatus,
    242     /* .pfnStreamPlay         =*/ drvHostNullAudioHA_StreamPlay,
    243     /* .pfnStreamCapture      =*/ drvHostNullAudioHA_StreamCapture,
     231    /* .pfnGetConfig                 =*/ drvHostNullAudioHA_GetConfig,
     232    /* .pfnGetDevices                =*/ NULL,
     233    /* .pfnGetStatus                 =*/ drvHostNullAudioHA_GetStatus,
     234    /* .pfnStreamConfigHint          =*/ NULL,
     235    /* .pfnStreamCreate              =*/ drvHostNullAudioHA_StreamCreate,
     236    /* .pfnStreamDestroy             =*/ drvHostNullAudioHA_StreamDestroy,
     237    /* .pfnStreamNotifyDeviceChanged =*/ NULL,
     238    /* .pfnStreamControl             =*/ drvHostNullAudioHA_StreamControl,
     239    /* .pfnStreamGetReadable         =*/ drvHostNullAudioHA_StreamGetReadable,
     240    /* .pfnStreamGetWritable         =*/ drvHostNullAudioHA_StreamGetWritable,
     241    /* .pfnStreamGetPending          =*/ drvHostNullAudioHA_StreamGetPending,
     242    /* .pfnStreamGetStatus           =*/ drvHostNullAudioHA_StreamGetStatus,
     243    /* .pfnStreamPlay                =*/ drvHostNullAudioHA_StreamPlay,
     244    /* .pfnStreamCapture             =*/ drvHostNullAudioHA_StreamCapture,
    244245};
    245246
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