VirtualBox

Changeset 88663 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Apr 22, 2021 6:31:47 PM (4 years ago)
Author:
vboxsync
Message:

drvHostAudioWasApi: Implemented caching of audio clients as the IAudioClient::Initialize method may alone take >100 ms. bugref:9890

File:
1 edited

Legend:

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

    r88630 r88663  
    2828#include <functiondiscoverykeys_devpkey.h>
    2929#include <AudioSessionTypes.h>
     30#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
     31# define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY UINT32_C(0x08000000)
     32#endif
     33#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
     34# define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM UINT32_C(0x80000000)
     35#endif
    3036
    3137#include <VBox/vmm/pdmaudioinline.h>
     
    5258class DrvHostAudioWasMmNotifyClient;
    5359
     60/** Pointer to the cache entry for a host audio device (+dir). */
     61typedef struct DRVHOSTAUDIOWASCACHEDEV *PDRVHOSTAUDIOWASCACHEDEV;
     62
     63/**
     64 * Cached pre-initialized audio client for a device.
     65 *
     66 * The activation and initialization of an IAudioClient has been observed to be
     67 * very very slow (> 100 ms) and not suitable to be done on an EMT.  So, we'll
     68 * pre-initialize the device clients at construction time and when the default
     69 * device changes to try avoid this problem.
     70 *
     71 * A client is returned to the cache after we're done with it, provided it still
     72 * works fine.
     73 */
     74typedef struct DRVHOSTAUDIOWASCACHEDEVCFG
     75{
     76    /** Entry in DRVHOSTAUDIOWASCACHEDEV::ConfigList. */
     77    RTLISTNODE                  ListEntry;
     78    /** The device.   */
     79    PDRVHOSTAUDIOWASCACHEDEV    pDevEntry;
     80    /** The cached audio client. */
     81    IAudioClient               *pIAudioClient;
     82    /** Output streams: The render client interface. */
     83    IAudioRenderClient         *pIAudioRenderClient;
     84    /** Input streams: The capture client interface. */
     85    IAudioCaptureClient        *pIAudioCaptureClient;
     86    /** The configuration. */
     87    PDMAUDIOPCMPROPS            Props;
     88    /** The buffer size in frames. */
     89    uint32_t                    cFramesBufferSize;
     90    /** The device/whatever period in frames. */
     91    uint32_t                    cFramesPeriod;
     92    /** The stringified properties. */
     93    char                        szProps[32];
     94} DRVHOSTAUDIOWASCACHEDEVCFG;
     95/** Pointer to a pre-initialized audio client. */
     96typedef DRVHOSTAUDIOWASCACHEDEVCFG *PDRVHOSTAUDIOWASCACHEDEVCFG;
     97
     98/**
     99 * Per audio device (+ direction) cache entry.
     100 */
     101typedef struct DRVHOSTAUDIOWASCACHEDEV
     102{
     103    /** Entry in DRVHOSTAUDIOWAS::CacheHead. */
     104    RTLISTNODE                  ListEntry;
     105    /** The MM device associated with the stream. */
     106    IMMDevice                  *pIDevice;
     107    /** The direction of the device. */
     108    PDMAUDIODIR                 enmDir;
     109#if 0 /* According to https://social.msdn.microsoft.com/Forums/en-US/1d974d90-6636-4121-bba3-a8861d9ab92a,
     110         these were always support just missing from the SDK. */
     111    /** Support for AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM: -1=unknown, 0=no, 1=yes. */
     112    int8_t                      fSupportsAutoConvertPcm;
     113    /** Support for AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY: -1=unknown, 0=no, 1=yes. */
     114    int8_t                      fSupportsSrcDefaultQuality;
     115#endif
     116    /** List of cached configurations (DRVHOSTAUDIOWASCACHEDEVCFG). */
     117    RTLISTANCHOR                ConfigList;
     118    /** The device ID length in RTUTF16 units. */
     119    size_t                      cwcDevId;
     120    /** The device ID. */
     121    RTUTF16                     wszDevId[RT_FLEXIBLE_ARRAY];
     122} DRVHOSTAUDIOWASCACHEDEV;
     123
     124
    54125/**
    55126 * Data for a WASABI stream.
     
    57128typedef struct DRVHOSTAUDIOWASSTREAM
    58129{
    59     /** Entry in DRVHOSTAUDIOWAS::HeadStreams. */
    60     RTLISTNODE              ListEntry;
     130    /** Entry in DRVHOSTAUDIOWAS::StreamHead. */
     131    RTLISTNODE                  ListEntry;
    61132    /** The stream's acquired configuration. */
    62     PDMAUDIOSTREAMCFG       Cfg;
    63     /** The MM device associated with the stream. */
    64     IMMDevice              *pIDevice;
    65     /** The audio client assoicated with the stream. */
    66     IAudioClient           *pIAudioClient;
    67     /** Output streams: The render client interface. */
    68     IAudioRenderClient     *pIAudioRenderClient;
    69     /** Input streams: The capture client interface. */
    70     IAudioCaptureClient    *pIAudioCaptureClient;
     133    PDMAUDIOSTREAMCFG           Cfg;
     134    /** Cache entry to be relased when destroying the stream. */
     135    PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg;
    71136
    72137    /** Set if the stream is enabled. */
    73     bool                    fEnabled;
     138    bool                        fEnabled;
    74139    /** Set if the stream is started (playing/capturing). */
    75     bool                    fStarted;
     140    bool                        fStarted;
    76141    /** Set if the stream is draining (output only). */
    77     bool                    fDraining;
     142    bool                        fDraining;
    78143    /** Set if we should restart the stream on resume (saved pause state). */
    79     bool                    fRestartOnResume;
     144    bool                        fRestartOnResume;
    80145
    81146    /** The RTTimeMilliTS() deadline for the draining of this stream (output). */
    82     uint64_t                msDrainDeadline;
     147    uint64_t                    msDrainDeadline;
    83148    /** Internal stream offset (bytes). */
    84     uint64_t                offInternal;
     149    uint64_t                    offInternal;
    85150    /** The RTTimeMilliTS() at the end of the last transfer. */
    86     uint64_t                msLastTransfer;
     151    uint64_t                    msLastTransfer;
    87152
    88153    /** Input: Current capture buffer (advanced as we read). */
    89     uint8_t                *pbCapture;
     154    uint8_t                    *pbCapture;
    90155    /** Input: The number of bytes left in the current capture buffer. */
    91     uint32_t                cbCapture;
     156    uint32_t                    cbCapture;
    92157    /** Input: The full size of what pbCapture is part of (for ReleaseBuffer). */
    93     uint32_t                cFramesCaptureToRelease;
     158    uint32_t                    cFramesCaptureToRelease;
    94159
    95160    /** Critical section protecting: . */
    96     RTCRITSECT              CritSect;
     161    RTCRITSECT                  CritSect;
    97162    /** Buffer that drvHostWasStreamStatusString uses. */
    98     char                    szStatus[128];
     163    char                        szStatus[128];
    99164} DRVHOSTAUDIOWASSTREAM;
    100165/** Pointer to a WASABI stream. */
     
    141206    /** List of streams (DRVHOSTAUDIOWASSTREAM).
    142207     * Requires CritSect ownership.  */
    143     RTLISTANCHOR                    HeadStreams;
    144     /** Serializing access to HeadStreams. */
    145     RTCRITSECTRW                    CritSectList;
     208    RTLISTANCHOR                    StreamHead;
     209    /** Serializing access to StreamHead. */
     210    RTCRITSECTRW                    CritSectStreamList;
    146211
    147212    /** Pointer to the MM notification client instance. */
    148213    DrvHostAudioWasMmNotifyClient  *pNotifyClient;
     214    /** List of cached devices (DRVHOSTAUDIOWASCACHEDEV).
     215     * Protected by CritSectCache  */
     216    RTLISTANCHOR                    CacheHead;
     217    /** Serializing access to CacheHead. */
     218    RTCRITSECT                      CritSectCache;
     219
    149220} DRVHOSTAUDIOWAS;
    150221/** Pointer to the data for a WASAPI host audio driver instance. */
     
    191262    pStreamWas->szStatus[off] = '\0';
    192263    return pStreamWas->szStatus;
     264}
     265
     266
     267/*********************************************************************************************************************************
     268*   Pre-activated audio device client cache.                                                                                     *
     269*********************************************************************************************************************************/
     270#define WAS_CACHE_MAX_ENTRIES_SAME_DEVICE   2
     271
     272/**
     273 * Converts from PDM stream config to windows WAVEFORMATEX struct.
     274 *
     275 * @param   pCfg    The PDM audio stream config to convert from.
     276 * @param   pFmt    The windows structure to initialize.
     277 */
     278static void drvHostAudioWasWaveFmtExFromCfg(PCPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt)
     279{
     280    RT_ZERO(*pFmt);
     281    pFmt->wFormatTag      = WAVE_FORMAT_PCM;
     282    pFmt->nChannels       = PDMAudioPropsChannels(&pCfg->Props);
     283    pFmt->wBitsPerSample  = PDMAudioPropsSampleBits(&pCfg->Props);
     284    pFmt->nSamplesPerSec  = PDMAudioPropsHz(&pCfg->Props);
     285    pFmt->nBlockAlign     = PDMAudioPropsFrameSize(&pCfg->Props);
     286    pFmt->nAvgBytesPerSec = PDMAudioPropsFramesToBytes(&pCfg->Props, PDMAudioPropsHz(&pCfg->Props));
     287    pFmt->cbSize          = 0; /* No extra data specified. */
     288}
     289
     290
     291/**
     292 * Converts from windows WAVEFORMATEX and stream props to PDM audio properties.
     293 *
     294 * @returns VINF_SUCCESS on success, VERR_AUDIO_STREAM_COULD_NOT_CREATE if not
     295 *          supported.
     296 * @param   pCfg        The stream configuration to update (input:
     297 *                      requested config; output: acquired).
     298 * @param   pFmt        The windows wave format structure.
     299 * @param   pszStream   The stream name for error logging.
     300 * @param   pwszDevId   The device ID for error logging.
     301 */
     302static int drvHostAudioWasCacheWaveFmtExToProps(PPDMAUDIOPCMPROPS pProps, WAVEFORMATEX const *pFmt,
     303                                                const char *pszStream, PCRTUTF16 pwszDevId)
     304{
     305    if (pFmt->wFormatTag == WAVE_FORMAT_PCM)
     306    {
     307        if (   pFmt->wBitsPerSample == 8
     308            || pFmt->wBitsPerSample == 16
     309            || pFmt->wBitsPerSample == 32)
     310        {
     311            if (pFmt->nChannels > 0 && pFmt->nChannels < 16)
     312            {
     313                if (pFmt->nSamplesPerSec >= 4096 && pFmt->nSamplesPerSec <= 768000)
     314                {
     315                    PDMAudioPropsInit(pProps, pFmt->wBitsPerSample / 8, true /*fSigned*/, pFmt->nChannels, pFmt->nSamplesPerSec);
     316                    if (PDMAudioPropsFrameSize(pProps) == pFmt->nBlockAlign)
     317                        return VINF_SUCCESS;
     318                }
     319            }
     320        }
     321    }
     322    LogRelMax(64, ("WasAPI: Error! Unsupported stream format for '%s' suggested by '%ls':\n"
     323                   "WasAPI:   wFormatTag      = %RU16 (expected %d)\n"
     324                   "WasAPI:   nChannels       = %RU16 (expected 1..15)\n"
     325                   "WasAPI:   nSamplesPerSec  = %RU32 (expected 4096..768000)\n"
     326                   "WasAPI:   nAvgBytesPerSec = %RU32\n"
     327                   "WasAPI:   nBlockAlign     = %RU16\n"
     328                   "WasAPI:   wBitsPerSample  = %RU16 (expected 8, 16, or 32)\n"
     329                   "WasAPI:   cbSize          = %RU16\n",
     330                   pszStream, pwszDevId, pFmt->wFormatTag, WAVE_FORMAT_PCM, pFmt->nChannels, pFmt->nSamplesPerSec, pFmt->nAvgBytesPerSec,
     331                   pFmt->nBlockAlign, pFmt->wBitsPerSample, pFmt->cbSize));
     332    return VERR_AUDIO_STREAM_COULD_NOT_CREATE;
     333}
     334
     335
     336/**
     337 * Destroys a devie config cache entry.
     338 *
     339 * @param   pDevCfg     Device config entry.  Must not be in the list.
     340 */
     341static void drvHostAudioWasCacheDestroyDevConfig(PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg)
     342{
     343    uint32_t cTypeClientRefs = 0;
     344    if (pDevCfg->pIAudioCaptureClient)
     345    {
     346        cTypeClientRefs = pDevCfg->pIAudioCaptureClient->Release();
     347        pDevCfg->pIAudioCaptureClient = NULL;
     348    }
     349
     350    if (pDevCfg->pIAudioRenderClient)
     351    {
     352        cTypeClientRefs = pDevCfg->pIAudioRenderClient->Release();
     353        pDevCfg->pIAudioRenderClient = NULL;
     354    }
     355
     356    uint32_t cClientRefs = 0;
     357    if (pDevCfg->pIAudioClient /* paranoia */)
     358    {
     359        cClientRefs = pDevCfg->pIAudioClient->Release();
     360        pDevCfg->pIAudioClient = NULL;
     361    }
     362
     363    Log8Func(("Destroying cache config entry: '%ls: %s' - cClientRefs=%u cTypeClientRefs=%u\n",
     364              pDevCfg->pDevEntry->wszDevId, pDevCfg->szProps, cClientRefs, cTypeClientRefs));
     365    RT_NOREF(cClientRefs, cTypeClientRefs);
     366
     367    pDevCfg->pDevEntry = NULL;
     368    RTMemFree(pDevCfg);
     369}
     370
     371
     372/**
     373 * Destroys a device cache entry.
     374 *
     375 * @param   pDevEntry   The device entry. Must not be in the cache!
     376 */
     377static void drvHostAudioWasCacheDestroyDevEntry(PDRVHOSTAUDIOWASCACHEDEV pDevEntry)
     378{
     379    Log8Func(("Destroying cache entry: %p - '%ls'\n", pDevEntry, pDevEntry->wszDevId));
     380
     381    PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg, pDevCfgNext;
     382    RTListForEachSafe(&pDevEntry->ConfigList, pDevCfg, pDevCfgNext, DRVHOSTAUDIOWASCACHEDEVCFG, ListEntry)
     383    {
     384        drvHostAudioWasCacheDestroyDevConfig(pDevCfg);
     385    }
     386
     387    uint32_t cDevRefs = 0;
     388    if (pDevEntry->pIDevice /* paranoia */)
     389    {
     390        cDevRefs = pDevEntry->pIDevice->Release();
     391        pDevEntry->pIDevice = NULL;
     392    }
     393
     394    pDevEntry->cwcDevId = 0;
     395    pDevEntry->wszDevId[0] = '\0';
     396    RTMemFree(pDevEntry);
     397    Log8Func(("Destroyed cache entry: %p cDevRefs=%u\n", pDevEntry, cDevRefs));
     398}
     399
     400
     401/**
     402 * Purges all the entries in the cache.
     403 */
     404static void drvHostAudioWasCachePurge(PDRVHOSTAUDIOWAS pThis)
     405{
     406    for (;;)
     407    {
     408        RTCritSectEnter(&pThis->CritSectCache);
     409        PDRVHOSTAUDIOWASCACHEDEV pDevEntry = RTListRemoveFirst(&pThis->CacheHead, DRVHOSTAUDIOWASCACHEDEV, ListEntry);
     410        RTCritSectLeave(&pThis->CritSectCache);
     411        if (!pDevEntry)
     412            break;
     413        drvHostAudioWasCacheDestroyDevEntry(pDevEntry);
     414    }
     415}
     416
     417
     418/**
     419 * Looks up a specific configuration.
     420 *
     421 * @returns Pointer to the device config (removed from cache) on success.  NULL
     422 *          if no matching config found.
     423 * @param   pDevEntry       Where to perform the lookup.
     424 * @param   pProp           The config properties to match.
     425 */
     426static PDRVHOSTAUDIOWASCACHEDEVCFG
     427drvHostAudioWasCacheLookupLocked(PDRVHOSTAUDIOWASCACHEDEV pDevEntry, PCPDMAUDIOPCMPROPS pProps)
     428{
     429    PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg;
     430    RTListForEach(&pDevEntry->ConfigList, pDevCfg, DRVHOSTAUDIOWASCACHEDEVCFG, ListEntry)
     431    {
     432        if (PDMAudioPropsAreEqual(&pDevCfg->Props, pProps))
     433        {
     434            RTListNodeRemove(&pDevCfg->ListEntry);
     435            return pDevCfg;
     436        }
     437    }
     438    return NULL;
     439}
     440
     441/**
     442 * Creates a device config entry using the given parameters.
     443 *
     444 * The entry is not added to the cache but returned.
     445 *
     446 * @returns Pointer to the new device config entry. NULL on failure.
     447 * @param   pDevEntry       The device entry it belongs to.
     448 * @param   pCfgReq         The requested configuration.
     449 * @param   pWaveFmtEx      The actual configuration.
     450 * @param   pIAudioClient   The audio client, reference consumed.
     451 */
     452static PDRVHOSTAUDIOWASCACHEDEVCFG
     453drvHostAudioWasCacheCreateConfig(PDRVHOSTAUDIOWASCACHEDEV pDevEntry, PCPDMAUDIOSTREAMCFG pCfgReq,
     454                                 WAVEFORMATEX const *pWaveFmtEx, IAudioClient *pIAudioClient)
     455{
     456    PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg = (PDRVHOSTAUDIOWASCACHEDEVCFG)RTMemAllocZ(sizeof(*pDevCfg));
     457    if (pDevCfg)
     458    {
     459        RTListInit(&pDevCfg->ListEntry);
     460        pDevCfg->pDevEntry          = pDevEntry;
     461        pDevCfg->pIAudioClient      = pIAudioClient;
     462        HRESULT hrc;
     463        if (pCfgReq->enmDir == PDMAUDIODIR_IN)
     464            hrc = pIAudioClient->GetService(__uuidof(IAudioCaptureClient), (void **)&pDevCfg->pIAudioCaptureClient);
     465        else
     466            hrc = pIAudioClient->GetService(__uuidof(IAudioRenderClient), (void **)&pDevCfg->pIAudioRenderClient);
     467        Log8Func(("GetService -> %Rhrc + %p\n", hrc, pCfgReq->enmDir == PDMAUDIODIR_IN
     468                  ? (void *)pDevCfg->pIAudioCaptureClient : (void *)pDevCfg->pIAudioRenderClient));
     469        if (SUCCEEDED(hrc))
     470        {
     471            /*
     472             * Obtain the actual stream format and buffer config.
     473             * (A bit ugly structure here to keep it from hitting the right margin. Sorry.)
     474             */
     475            UINT32          cFramesBufferSize       = 0;
     476            REFERENCE_TIME  cDefaultPeriodInNtTicks = 0;
     477            REFERENCE_TIME  cMinimumPeriodInNtTicks = 0;
     478            REFERENCE_TIME  cLatencyinNtTicks       = 0;
     479            hrc = pIAudioClient->GetBufferSize(&cFramesBufferSize);
     480            if (SUCCEEDED(hrc))
     481                hrc = pIAudioClient->GetDevicePeriod(&cDefaultPeriodInNtTicks, &cMinimumPeriodInNtTicks);
     482            else
     483                LogRelMax(64, ("WasAPI: GetBufferSize failed: %Rhrc\n", hrc));
     484            if (SUCCEEDED(hrc))
     485                hrc = pIAudioClient->GetStreamLatency(&cLatencyinNtTicks);
     486            else
     487                LogRelMax(64, ("WasAPI: GetDevicePeriod failed: %Rhrc\n", hrc));
     488            if (SUCCEEDED(hrc))
     489            {
     490                LogRel2(("WasAPI: Aquired buffer parameters for %s:\n"
     491                         "WasAPI:   cFramesBufferSize       = %RU32\n"
     492                         "WasAPI:   cDefaultPeriodInNtTicks = %RI64\n"
     493                         "WasAPI:   cMinimumPeriodInNtTicks = %RI64\n"
     494                         "WasAPI:   cLatencyinNtTicks       = %RI64\n",
     495                         pCfgReq->szName, cFramesBufferSize, cDefaultPeriodInNtTicks, cMinimumPeriodInNtTicks, cLatencyinNtTicks));
     496
     497                int rc = drvHostAudioWasCacheWaveFmtExToProps(&pDevCfg->Props, pWaveFmtEx, pCfgReq->szName, pDevEntry->wszDevId);
     498                if (RT_SUCCESS(rc))
     499                {
     500                    pDevCfg->cFramesBufferSize = cFramesBufferSize;
     501                    pDevCfg->cFramesPeriod     = PDMAudioPropsNanoToFrames(&pDevCfg->Props, cDefaultPeriodInNtTicks * 100);
     502
     503                    PDMAudioPropsToString(&pDevCfg->Props, pDevCfg->szProps, sizeof(pDevCfg->szProps));
     504                    return pDevCfg;
     505                }
     506            }
     507            else
     508                LogRelMax(64, ("WasAPI: GetStreamLatency failed: %Rhrc\n", hrc));
     509
     510            if (pDevCfg->pIAudioCaptureClient)
     511            {
     512                pDevCfg->pIAudioCaptureClient->Release();
     513                pDevCfg->pIAudioCaptureClient = NULL;
     514            }
     515
     516            if (pDevCfg->pIAudioRenderClient)
     517            {
     518                pDevCfg->pIAudioRenderClient->Release();
     519                pDevCfg->pIAudioRenderClient = NULL;
     520            }
     521        }
     522        RTMemFree(pDevCfg);
     523    }
     524    pIAudioClient->Release();
     525    return NULL;
     526}
     527
     528
     529/**
     530 * Worker for drvHostAudioWasCacheLookupOrCreate.
     531 *
     532 * If lookup fails, a new entry will be created.
     533 *
     534 * @note    Called holding the lock, returning without holding it!
     535 */
     536static PDRVHOSTAUDIOWASCACHEDEVCFG
     537drvHostAudioWasCacheLookupOrCreateConfig(PDRVHOSTAUDIOWAS pThis, PDRVHOSTAUDIOWASCACHEDEV pDevEntry, PCPDMAUDIOSTREAMCFG pCfgReq)
     538{
     539    char szProps[64];
     540    PDMAudioPropsToString(&pCfgReq->Props, szProps, sizeof(szProps));
     541
     542    /*
     543     * Check if we've got a matching config.
     544     */
     545    PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg = drvHostAudioWasCacheLookupLocked(pDevEntry, &pCfgReq->Props);
     546    if (pDevCfg)
     547    {
     548        RTCritSectLeave(&pThis->CritSectCache);
     549        Log8Func(("Config cache hit '%s' (for '%s') on '%ls': %p\n", pDevCfg->szProps, szProps, pDevEntry->wszDevId, pDevCfg));
     550        return pDevCfg;
     551    }
     552
     553    /*
     554     * We now need an IAudioClient interface for calling IsFormatSupported
     555     * on so we can get guidance as to what to do next.
     556     *
     557     * Initially, I thought the AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM was not
     558     * supported all the way back to Vista and that we'd had to try different
     559     * things here to get the most optimal format.  However, according to
     560     * https://social.msdn.microsoft.com/Forums/en-US/1d974d90-6636-4121-bba3-a8861d9ab92a
     561     * it is supported, just maybe missing from the SDK or something...
     562     */
     563    RTCritSectLeave(&pThis->CritSectCache);
     564
     565    REFERENCE_TIME const cBufferSizeInNtTicks = PDMAudioPropsFramesToNtTicks(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize);
     566
     567    Log8Func(("Activating an IAudioClient for '%ls' ...\n", pDevEntry->wszDevId));
     568    IAudioClient *pIAudioClient = NULL;
     569    HRESULT hrc = pDevEntry->pIDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL,
     570                                                NULL /*pActivationParams*/, (void **)&pIAudioClient);
     571    Log8Func(("Activate('%ls', IAudioClient) -> %Rhrc\n", pDevEntry->wszDevId, hrc));
     572    if (FAILED(hrc))
     573    {
     574        LogRelMax(64, ("WasAPI: Activate(%ls, IAudioClient) failed: %Rhrc\n", pDevEntry->wszDevId, hrc));
     575        return NULL;
     576    }
     577
     578    WAVEFORMATEX  WaveFmtEx;
     579    drvHostAudioWasWaveFmtExFromCfg(pCfgReq, &WaveFmtEx);
     580
     581    PWAVEFORMATEX pClosestMatch = NULL;
     582    hrc = pIAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &WaveFmtEx, &pClosestMatch);
     583
     584    /*
     585     * If the format is supported, create a cache entry for it.
     586     */
     587    if (SUCCEEDED(hrc))
     588    {
     589        if (hrc == S_OK)
     590            Log8Func(("IsFormatSupport(,%s,) -> S_OK + %p: requested format is supported\n", szProps, pClosestMatch));
     591        else
     592            Log8Func(("IsFormatSupport(,%s,) -> %Rhrc + %p: %uch S%u %uHz\n", szProps, hrc, pClosestMatch,
     593                      pClosestMatch ? pClosestMatch->nChannels : 0, pClosestMatch ? pClosestMatch->wBitsPerSample : 0,
     594                      pClosestMatch ? pClosestMatch->nSamplesPerSec : 0));
     595
     596        uint32_t fInitFlags = AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
     597                            | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
     598        hrc = pIAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, fInitFlags, cBufferSizeInNtTicks,
     599                                        0 /*cPeriodicityInNtTicks*/, &WaveFmtEx, NULL /*pAudioSessionGuid*/);
     600        Log8Func(("Initialize(,%x, %RI64, %s,) -> %Rhrc\n", fInitFlags, cBufferSizeInNtTicks, szProps, hrc));
     601        if (SUCCEEDED(hrc))
     602        {
     603            if (pClosestMatch)
     604                CoTaskMemFree(pClosestMatch);
     605            Log8Func(("Creating new config for '%s' on '%ls': %p\n", szProps, pDevEntry->wszDevId, pDevCfg));
     606            return drvHostAudioWasCacheCreateConfig(pDevEntry, pCfgReq, &WaveFmtEx, pIAudioClient);
     607        }
     608
     609        LogRelMax(64, ("WasAPI: IAudioClient::Initialize(%s: %s) failed: %Rhrc\n", pCfgReq->szName, szProps, hrc));
     610
     611#if 0 /* later if needed */
     612        /*
     613         * Try lookup or instantiate the closest config.
     614         */
     615        PDMAUDIOSTREAMCFG ClosestCfg = *pCfgReq;
     616        int rc = drvHostAudioWasCacheWaveFmtExToProps(&ClosestCfg.Props, pClosestMatch, pDevEntry->wszDevId);
     617        if (RT_SUCCESS(rc))
     618        {
     619            RTCritSectEnter(&pThis->CritSectCache);
     620            pDevCfg = drvHostAudioWasCacheLookupLocked(pDevEntry, &pCfgReq->Props);
     621            if (pDevCfg)
     622            {
     623                CoTaskMemFree(pClosestMatch);
     624                Log8Func(("Config cache hit '%s' (for '%s') on '%ls': %p\n", pDevCfg->szProps, szProps, pDevEntry->wszDevId, pDevCfg));
     625                return pDevCfg;
     626            }
     627            RTCritSectLeave(&pThis->CritSectCache);
     628        }
     629#endif
     630    }
     631    else
     632        LogRelMax(64,("WasAPI: IAudioClient::IsFormatSupport(,%s: %s,) failed: %Rhrc\n", pCfgReq->szName, szProps, hrc));
     633
     634    pIAudioClient->Release();
     635    if (pClosestMatch)
     636        CoTaskMemFree(pClosestMatch);
     637    Log8Func(("returns NULL\n"));
     638    return NULL;
     639}
     640
     641
     642/**
     643 * Looks up the given device + config combo in the cache, creating a new entry
     644 * if missing.
     645 *
     646 * @returns Pointer to the requested device config (or closest alternative).
     647 *          NULL on failure (TODO: need to return why).
     648 * @param   pThis       The WASAPI host audio driver instance data.
     649 * @param   pIDevice    The device to look up.
     650 * @param   pCfgReq     The configuration to look up.
     651 */
     652static PDRVHOSTAUDIOWASCACHEDEVCFG
     653drvHostAudioWasCacheLookupOrCreate(PDRVHOSTAUDIOWAS pThis, IMMDevice *pIDevice, PCPDMAUDIOSTREAMCFG pCfgReq)
     654{
     655    /*
     656     * Get the device ID so we can perform the lookup.
     657     */
     658    LPWSTR  pwszDevId = NULL;
     659    HRESULT hrc = pIDevice->GetId(&pwszDevId);
     660    if (SUCCEEDED(hrc))
     661    {
     662        size_t cwcDevId = RTUtf16Len(pwszDevId);
     663
     664        /*
     665         * The cache has two levels, so first the device entry.
     666         */
     667        PDRVHOSTAUDIOWASCACHEDEV pDevEntry;
     668        RTCritSectEnter(&pThis->CritSectCache);
     669        RTListForEach(&pThis->CacheHead, pDevEntry, DRVHOSTAUDIOWASCACHEDEV, ListEntry)
     670        {
     671            if (   pDevEntry->cwcDevId == cwcDevId
     672                && pDevEntry->enmDir   == pCfgReq->enmDir
     673                && RTUtf16Cmp(pDevEntry->wszDevId, pwszDevId) == 0)
     674            {
     675                CoTaskMemFree(pwszDevId);
     676                Log8Func(("Cache hit for device '%ls': %p\n", pDevEntry->wszDevId, pDevEntry));
     677                return drvHostAudioWasCacheLookupOrCreateConfig(pThis, pDevEntry, pCfgReq);
     678            }
     679        }
     680        RTCritSectLeave(&pThis->CritSectCache);
     681
     682        /*
     683         * Device not in the cache, add it.
     684         */
     685        pDevEntry = (PDRVHOSTAUDIOWASCACHEDEV)RTMemAllocZVar(RT_UOFFSETOF_DYN(DRVHOSTAUDIOWASCACHEDEV, wszDevId[cwcDevId + 1]));
     686        if (pDevEntry)
     687        {
     688            pIDevice->AddRef();
     689            pDevEntry->pIDevice                   = pIDevice;
     690            pDevEntry->enmDir                     = pCfgReq->enmDir;
     691            pDevEntry->cwcDevId                   = cwcDevId;
     692#if 0
     693            pDevEntry->fSupportsAutoConvertPcm    = -1;
     694            pDevEntry->fSupportsSrcDefaultQuality = -1;
     695#endif
     696            RTListInit(&pDevEntry->ConfigList);
     697            memcpy(pDevEntry->wszDevId, pwszDevId, cwcDevId * sizeof(RTUTF16));
     698            pDevEntry->wszDevId[cwcDevId] = '\0';
     699
     700            CoTaskMemFree(pwszDevId);
     701            pwszDevId = NULL;
     702
     703            /*
     704             * Before adding the device, check that someone didn't race us adding it.
     705             */
     706            RTCritSectEnter(&pThis->CritSectCache);
     707            PDRVHOSTAUDIOWASCACHEDEV pDevEntry2;
     708            RTListForEach(&pThis->CacheHead, pDevEntry2, DRVHOSTAUDIOWASCACHEDEV, ListEntry)
     709            {
     710                if (   pDevEntry2->cwcDevId == cwcDevId
     711                    && pDevEntry2->enmDir   == pCfgReq->enmDir
     712                    && RTUtf16Cmp(pDevEntry2->wszDevId, pDevEntry->wszDevId) == 0)
     713                {
     714                    pIDevice->Release();
     715                    RTMemFree(pDevEntry);
     716                    pDevEntry = NULL;
     717
     718                    Log8Func(("Lost race adding device '%ls': %p\n", pDevEntry2->wszDevId, pDevEntry2));
     719                    return drvHostAudioWasCacheLookupOrCreateConfig(pThis, pDevEntry2, pCfgReq);
     720                }
     721            }
     722            RTListPrepend(&pThis->CacheHead, &pDevEntry->ListEntry);
     723
     724            Log8Func(("Added device '%ls' to cache: %p\n", pDevEntry->wszDevId, pDevEntry));
     725            return drvHostAudioWasCacheLookupOrCreateConfig(pThis, pDevEntry, pCfgReq);
     726        }
     727        CoTaskMemFree(pwszDevId);
     728    }
     729    else
     730        LogRelMax(64, ("WasAPI: GetId failed (lookup): %Rhrc\n", hrc));
     731    return NULL;
     732}
     733
     734
     735/**
     736 * Return the given config to the cache.
     737 *
     738 * @param   pThis       The WASAPI host audio driver instance data.
     739 * @param   pDevCfg     The device config to put back.
     740 */
     741static void drvHostAudioWasCachePutBack(PDRVHOSTAUDIOWAS pThis, PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg)
     742{
     743    /*
     744     * Reset the audio client to see that it works and to make sure it's in a sensible state.
     745     */
     746    HRESULT hrc = pDevCfg->pIAudioClient->Reset();
     747    if (SUCCEEDED(hrc))
     748    {
     749        Log8Func(("Putting %p/'%s' back\n", pDevCfg, pDevCfg->szProps));
     750        RTCritSectEnter(&pThis->CritSectCache);
     751        RTListAppend(&pDevCfg->pDevEntry->ConfigList, &pDevCfg->ListEntry);
     752        RTCritSectLeave(&pThis->CritSectCache);
     753    }
     754    else
     755    {
     756        Log8Func(("IAudioClient::Reset failed (%Rhrc) on %p/'%s', destroying it.\n", hrc, pDevCfg, pDevCfg->szProps));
     757        drvHostAudioWasCacheDestroyDevConfig(pDevCfg);
     758    }
     759}
     760
     761
     762/**
     763 * Prefills the cache.
     764 *
     765 * @param   pThis       The WASAPI host audio driver instance data.
     766 */
     767static void drvHostAudioWasCacheFill(PDRVHOSTAUDIOWAS pThis)
     768{
     769#if 0 /* we don't have the buffer config nor do we really know which frequences to expect */
     770    Log8Func(("enter\n"));
     771    struct
     772    {
     773        PCRTUTF16       pwszDevId;
     774        PDMAUDIODIR     enmDir;
     775    } aToCache[] =
     776    {
     777        { pThis->pwszInputDevId, PDMAUDIODIR_IN },
     778        { pThis->pwszOutputDevId, PDMAUDIODIR_OUT }
     779    };
     780    for (unsigned i = 0; i < RT_ELEMENTS(aToCache); i++)
     781    {
     782        PCRTUTF16       pwszDevId = aToCache[i].pwszDevId;
     783        IMMDevice      *pIDevice  = NULL;
     784        HRESULT         hrc;
     785        if (pwszDevId)
     786            hrc = pThis->pIEnumerator->GetDevice(pwszDevId, &pIDevice);
     787        else
     788        {
     789            hrc = pThis->pIEnumerator->GetDefaultAudioEndpoint(aToCache[i].enmDir == PDMAUDIODIR_IN ? eCapture : eRender,
     790                                                               eMultimedia, &pIDevice);
     791            pwszDevId = aToCache[i].enmDir == PDMAUDIODIR_IN ? L"{Default-In}" : L"{Default-Out}";
     792        }
     793        if (SUCCEEDED(hrc))
     794        {
     795            PDMAUDIOSTREAMCFG Cfg = { aToCache[i].enmDir, { PDMAUDIOPLAYBACKDST_INVALID },
     796                                      PDMAUDIOPCMPROPS_INITIALIZER(2, true, 2, 44100, false) };
     797            Cfg.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&Cfg.Props, 300);
     798            PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg = drvHostAudioWasCacheLookupOrCreate(pThis, pIDevice, &Cfg);
     799            if (pDevCfg)
     800                drvHostAudioWasCachePutBack(pThis, pDevCfg);
     801
     802            pIDevice->Release();
     803        }
     804        else
     805            LogRelMax(64, ("WasAPI: Failed to open audio device '%ls' (pre-caching): %Rhrc\n", pwszDevId, hrc));
     806    }
     807    Log8Func(("leave\n"));
     808#else
     809    RT_NOREF(pThis);
     810#endif
    193811}
    194812
     
    5331151
    5341152/**
    535  * Converts from PDM stream config to windows WAVEFORMATEX struct.
    536  *
    537  * @param   pCfg    The PDM audio stream config to convert from.
    538  * @param   pFmt    The windows structure to initialize.
    539  */
    540 static void drvHostAudioWasWaveFmtExFromCfg(PCPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt)
    541 {
    542     RT_ZERO(*pFmt);
    543     pFmt->wFormatTag      = WAVE_FORMAT_PCM;
    544     pFmt->nChannels       = PDMAudioPropsChannels(&pCfg->Props);
    545     pFmt->wBitsPerSample  = PDMAudioPropsSampleBits(&pCfg->Props);
    546     pFmt->nSamplesPerSec  = PDMAudioPropsHz(&pCfg->Props);
    547     pFmt->nBlockAlign     = PDMAudioPropsFrameSize(&pCfg->Props);
    548     pFmt->nAvgBytesPerSec = PDMAudioPropsFramesToBytes(&pCfg->Props, PDMAudioPropsHz(&pCfg->Props));
    549     pFmt->cbSize          = 0; /* No extra data specified. */
    550 }
    551 
    552 
    553 /**
    554  * Converts from windows WAVEFORMATEX and stream props to PDM stream config.
    555  *
    556  * @returns VINF_SUCCESS on success, VERR_AUDIO_STREAM_COULD_NOT_CREATE if not
    557  *          supported.
    558  * @param   pCfg                    The stream configuration to update (input:
    559  *                                  requested config; output: acquired).
    560  * @param   pFmt                    The windows wave format structure.
    561  * @param   cFramesBufferSize       The actual buffer size in frames.
    562  * @param   cDefaultPeriodInNtTicks The default device period in 100ns NT ticks.
    563  */
    564 static int drvHostAudioWasWaveFmtExToCfgAck(PPDMAUDIOSTREAMCFG pCfg, WAVEFORMATEX const *pFmt,
    565                                             uint32_t cFramesBufferSize, int64_t cDefaultPeriodInNtTicks)
    566 {
    567 #if 0 /* pFmt is the mixer format, not the stream format. duh. */
    568     if (pFmt->wFormatTag == WAVE_FORMAT_PCM)
    569     {
    570         if (   pFmt->wBitsPerSample == 8
    571             || pFmt->wBitsPerSample == 16
    572             || pFmt->wBitsPerSample == 32)
    573         {
    574             if (pFmt->nChannels > 0 && pFmt->nChannels < 16)
    575             {
    576                 if (pFmt->nSamplesPerSec >= 4096 && pFmt->nSamplesPerSec <= 768000)
    577                 {
    578                     PDMAudioPropsInit(&pCfg->Props, pFmt->wBitsPerSample / 8, true /*fSigned*/,
    579                                       pFmt->nChannels, pFmt->nSamplesPerSec);
    580                     if (PDMAudioPropsFrameSize(&pCfg->Props) == pFmt->nBlockAlign)
    581                     {
    582 #endif
    583                         if (PDMAudioPropsAreValid(&pCfg->Props))
    584                         {
    585                             pCfg->Backend.cFramesPreBuffering = pCfg->Backend.cFramesPreBuffering * cFramesBufferSize
    586                                                               / RT_MAX(pCfg->Backend.cFramesBufferSize, 1);
    587                             pCfg->Backend.cFramesBufferSize   = cFramesBufferSize;
    588                             pCfg->Backend.cFramesPeriod       = PDMAudioPropsNanoToFrames(&pCfg->Props,
    589                                                                                           cDefaultPeriodInNtTicks * 100);
    590                             return VINF_SUCCESS;
    591                         }
    592 #if 0
    593                     }
    594                 }
    595             }
    596         }
    597     }
    598 #endif
    599     LogRelMax(64, ("WasAPI: Error! Unsupported stream format for '%s' acquired:\n"
    600                    "WasAPI:   wFormatTag      = %RU16 (expected %d)\n"
    601                    "WasAPI:   nChannels       = %RU16 (expected 1..15)\n"
    602                    "WasAPI:   nSamplesPerSec  = %RU32 (expected 4096..768000)\n"
    603                    "WasAPI:   nAvgBytesPerSec = %RU32\n"
    604                    "WasAPI:   nBlockAlign     = %RU16\n"
    605                    "WasAPI:   wBitsPerSample  = %RU16 (expected 8, 16, or 32)\n"
    606                    "WasAPI:   cbSize          = %RU16\n",
    607                    "WasAPI:   cFramesBufferSize       = %RU32\n"
    608                    "WasAPI:   cDefaultPeriodInNtTicks = %RI64\n",
    609                    pCfg->szName, pFmt->wFormatTag, WAVE_FORMAT_PCM, pFmt->nChannels, pFmt->nSamplesPerSec, pFmt->nAvgBytesPerSec,
    610                    pFmt->nBlockAlign, pFmt->wBitsPerSample, pFmt->cbSize, cFramesBufferSize, cDefaultPeriodInNtTicks));
    611     return VERR_AUDIO_STREAM_COULD_NOT_CREATE;
    612 }
    613 
    614 
    615 /**
    6161153 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
    6171154 */
     
    6421179     * Do configuration conversion.
    6431180     */
    644     REFERENCE_TIME const cBufferSizeInNtTicks = PDMAudioPropsFramesToNtTicks(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize);
    6451181    WAVEFORMATEX WaveFmtX;
    6461182    drvHostAudioWasWaveFmtExFromCfg(pCfgReq, &WaveFmtX);
     
    6551191             "WasAPI:   cBufferSizeInNtTicks = %RU64\n",
    6561192             pszStreamType, pCfgReq->szName, WaveFmtX.wFormatTag, WaveFmtX.nChannels, WaveFmtX.nSamplesPerSec,
    657              WaveFmtX.nAvgBytesPerSec, WaveFmtX.nBlockAlign, WaveFmtX.wBitsPerSample, WaveFmtX.cbSize, cBufferSizeInNtTicks));
     1193             WaveFmtX.nAvgBytesPerSec, WaveFmtX.nBlockAlign, WaveFmtX.wBitsPerSample, WaveFmtX.cbSize,
     1194             PDMAudioPropsFramesToNtTicks(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize) ));
    6581195
    6591196    /*
     
    6671204    else
    6681205    {
     1206        /** @todo this takes about 2ms. Cache it and update it using the
     1207         *        notification client. */
    6691208        hrc = pThis->pIEnumerator->GetDefaultAudioEndpoint(pCfgReq->enmDir == PDMAUDIODIR_IN ? eCapture : eRender,
    6701209                                                           eMultimedia, &pIDevice);
     
    6721211    }
    6731212    LogFlowFunc(("Got device %p (%Rhrc)\n", pIDevice, hrc));
    674     if (SUCCEEDED(hrc))
    675         pStreamWas->pIDevice = pIDevice;
    676     else
    677     {
    678         LogRelMax(64, ("WasAPI: Failed to open audio device '%ls': %Rhrc\n", pszStreamType, pwszDevId, hrc));
     1213    if (FAILED(hrc))
     1214    {
     1215        LogRelMax(64, ("WasAPI: Failed to open audio %s device '%ls': %Rhrc\n", pszStreamType, pwszDevId, hrc));
    6791216        return VERR_AUDIO_STREAM_COULD_NOT_CREATE;
    6801217    }
    6811218
    6821219    /*
    683      * Activate the desired interface.
    684      *
    685      * Note! This is _very_ expensive here on my system.  We will have to
    686      *       pre-init or offload this onto a thread and hope it will finishe
    687      *       before the prebuffering is done...
    688      */
    689     /** @todo this is too slow!   */
     1220     * Ask the cache to retrieve or instantiate the requested configuration.
     1221     */
     1222    /** @todo make it return a status code too and retry if the default device
     1223     *        was invalidated/changed while we where working on it here. */
    6901224    int rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
    691     IAudioClient *pIAudioClient = NULL;
    692     hrc = pIDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL /*pActivationParams*/, (void **)&pIAudioClient);
    693     LogFlowFunc(("Activate -> %Rhrc\n", hrc));
    694     if (SUCCEEDED(hrc))
    695     {
    696         pStreamWas->pIAudioClient = pIAudioClient;
    697 
    698         /*
    699          * Initialize the client.
    700          */
    701         uint32_t fInitFlags = 0x80000000;//AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
    702         hrc = pIAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
    703                                         fInitFlags,
    704                                         cBufferSizeInNtTicks,
    705                                         0 /*cPeriodicityInNtTicks*/,
    706                                         &WaveFmtX,
    707                                         NULL /*pAudioSessionGuid*/);
    708         LogFlowFunc(("Initialize -> %Rhrc\n", hrc));
    709         if (SUCCEEDED(hrc))
    710         {
    711             /*
    712              * Get the interface specific to the stream direction.
    713              */
    714             if (pCfgReq->enmDir == PDMAUDIODIR_IN)
    715                 hrc = pIAudioClient->GetService(__uuidof(IAudioCaptureClient), (void **)&pStreamWas->pIAudioCaptureClient);
    716             else
    717                 hrc = pIAudioClient->GetService(__uuidof(IAudioRenderClient), (void **)&pStreamWas->pIAudioRenderClient);
    718             LogFlowFunc(("GetService -> %Rhrc + %p\n", hrc, pCfgReq->enmDir == PDMAUDIODIR_IN
    719                          ? (void *)pStreamWas->pIAudioCaptureClient : (void *)pStreamWas->pIAudioRenderClient));
    720             if (SUCCEEDED(hrc))
    721             {
    722                 /*
    723                  * Obtain the actual stream format and buffer config.
    724                  * (A bit ugly structure here to keep it from hitting the right margin. Sorry.)
    725                  */
    726                 WAVEFORMATEX   *pActualWaveFmtX         = NULL;
    727                 UINT32          cFramesBufferSize       = 0;
    728                 REFERENCE_TIME  cDefaultPeriodInNtTicks = 0;
    729                 REFERENCE_TIME  cMinimumPeriodInNtTicks = 0;
    730                 REFERENCE_TIME  cLatencyinNtTicks       = 0;
    731                 hrc = pIAudioClient->GetMixFormat(&pActualWaveFmtX); /** @todo this is of little iterest.*/
    732                 if (SUCCEEDED(hrc))
    733                     hrc = pIAudioClient->GetBufferSize(&cFramesBufferSize);
    734                 else
    735                 {
    736                     LogRelMax(64, ("WasAPI: GetMixFormat failed: %Rhrc\n", hrc));
    737                     pActualWaveFmtX = NULL;
    738                 }
    739                 if (SUCCEEDED(hrc))
    740                     hrc = pIAudioClient->GetDevicePeriod(&cDefaultPeriodInNtTicks, &cMinimumPeriodInNtTicks);
    741                 else
    742                     LogRelMax(64, ("WasAPI: GetBufferSize failed: %Rhrc\n", hrc));
    743                 if (SUCCEEDED(hrc))
    744                     hrc = pIAudioClient->GetStreamLatency(&cLatencyinNtTicks);
    745                 else
    746                     LogRelMax(64, ("WasAPI: GetDevicePeriod failed: %Rhrc\n", hrc));
    747                 if (SUCCEEDED(hrc))
    748                 {
    749                     LogRel2(("WasAPI: Mixer %s format for '%s':\n"
    750                              "WasAPI:   wFormatTag      = %RU16\n"
    751                              "WasAPI:   nChannels       = %RU16\n"
    752                              "WasAPI:   nSamplesPerSec  = %RU32\n"
    753                              "WasAPI:   nAvgBytesPerSec = %RU32\n"
    754                              "WasAPI:   nBlockAlign     = %RU16\n"
    755                              "WasAPI:   wBitsPerSample  = %RU16\n"
    756                              "WasAPI:   cbSize          = %RU16\n"
    757                              "WasAPI: Aquired buffer parameters:\n"
    758                              "WasAPI:   cFramesBufferSize       = %RU32\n"
    759                              "WasAPI:   cDefaultPeriodInNtTicks = %RI64\n"
    760                              "WasAPI:   cMinimumPeriodInNtTicks = %RI64\n"
    761                              "WasAPI:   cLatencyinNtTicks       = %RI64\n",
    762                              pszStreamType, pCfgReq->szName, pActualWaveFmtX->wFormatTag, pActualWaveFmtX->nChannels,
    763                              pActualWaveFmtX->nSamplesPerSec, pActualWaveFmtX->nAvgBytesPerSec, pActualWaveFmtX->nBlockAlign,
    764                              pActualWaveFmtX->wBitsPerSample, pActualWaveFmtX->cbSize, cFramesBufferSize, cDefaultPeriodInNtTicks,
    765                              cMinimumPeriodInNtTicks, cLatencyinNtTicks));
    766                     rc = drvHostAudioWasWaveFmtExToCfgAck(pCfgAcq, pActualWaveFmtX, cFramesBufferSize, cDefaultPeriodInNtTicks);
    767                     if (RT_SUCCESS(rc))
    768                     {
    769                         PDMAudioStrmCfgCopy(&pStreamWas->Cfg, pCfgAcq);
    770 
    771                         /* Finally, the critical section. */
    772                         int rc2 = RTCritSectInit(&pStreamWas->CritSect);
    773                         if (RT_SUCCESS(rc2))
    774                         {
    775                             RTCritSectRwEnterExcl(&pThis->CritSectList);
    776                             RTListAppend(&pThis->HeadStreams, &pStreamWas->ListEntry);
    777                             RTCritSectRwLeaveExcl(&pThis->CritSectList);
    778 
    779                             rc = VINF_SUCCESS;
    780                         }
    781                         else
    782                             LogRelMax(64, ("WasAPI: Failed to create critical section for stream.\n", hrc));
    783                     }
    784                 }
    785                 else
    786                     LogRelMax(64, ("WasAPI: GetStreamLatency failed: %Rhrc\n", hrc));
    787 
    788                 if (pActualWaveFmtX)
    789                     CoTaskMemFree(pActualWaveFmtX);
    790             }
    791             else
    792                 LogRelMax(64, ("WasAPI: Failed to obtain %s servier for %s audio client (%ls): %Rhrc\n",
    793                                pCfgReq->enmDir == PDMAUDIODIR_IN ? "IAudioCaptureClient" : "IAudioRenderClient",
    794                                pszStreamType, pwszDevId, hrc));
    795         }
    796         else
    797             LogRelMax(64, ("WasAPI: Failed to initialize %s audio client (%ls): %Rhrc\n", pszStreamType, pwszDevId, hrc));
     1225    PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg = drvHostAudioWasCacheLookupOrCreate(pThis, pIDevice, pCfgReq);
     1226
     1227    pIDevice->Release();
     1228    pIDevice = NULL;
     1229
     1230    if (pDevCfg)
     1231    {
     1232        pStreamWas->pDevCfg = pDevCfg;
     1233
     1234        pCfgAcq->Props = pDevCfg->Props;
     1235        pCfgAcq->Backend.cFramesBufferSize   = pDevCfg->cFramesBufferSize;
     1236        pCfgAcq->Backend.cFramesPeriod       = pDevCfg->cFramesPeriod;
     1237        pCfgAcq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesPreBuffering * pDevCfg->cFramesBufferSize
     1238                                             / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);
     1239
     1240        PDMAudioStrmCfgCopy(&pStreamWas->Cfg, pCfgAcq);
     1241
     1242        /* Finally, the critical section. */
     1243        int rc2 = RTCritSectInit(&pStreamWas->CritSect);
     1244        if (RT_SUCCESS(rc2))
     1245        {
     1246            RTCritSectRwEnterExcl(&pThis->CritSectStreamList);
     1247            RTListAppend(&pThis->StreamHead, &pStreamWas->ListEntry);
     1248            RTCritSectRwLeaveExcl(&pThis->CritSectStreamList);
     1249
     1250            LogFlowFunc(("returns VINF_SUCCESS\n", rc));
     1251            return VINF_SUCCESS;
     1252        }
     1253
     1254        LogRelMax(64, ("WasAPI: Failed to create critical section for stream.\n", hrc));
     1255        drvHostAudioWasCachePutBack(pThis, pDevCfg);
     1256        pStreamWas->pDevCfg = NULL;
    7981257    }
    7991258    else
    8001259        LogRelMax(64, ("WasAPI: Failed to activate %s audio device '%ls': %Rhrc\n", pszStreamType, pwszDevId, hrc));
    801 
    802     if (RT_FAILURE(rc))
    803         pThis->IHostAudio.pfnStreamDestroy(pInterface, pStream);
    8041260
    8051261    LogFlowFunc(("returns %Rrc\n", rc));
     
    8211277    if (RTCritSectIsInitialized(&pStreamWas->CritSect))
    8221278    {
    823         RTCritSectRwEnterExcl(&pThis->CritSectList);
     1279        RTCritSectRwEnterExcl(&pThis->CritSectStreamList);
    8241280        RTListNodeRemove(&pStreamWas->ListEntry);
    825         RTCritSectRwLeaveExcl(&pThis->CritSectList);
     1281        RTCritSectRwLeaveExcl(&pThis->CritSectStreamList);
    8261282
    8271283        RTCritSectDelete(&pStreamWas->CritSect);
    8281284    }
    8291285
    830     if (pStreamWas->fStarted && pStreamWas->pIAudioClient)
    831     {
    832         hrc = pStreamWas->pIAudioClient->Stop();
     1286    if (pStreamWas->fStarted && pStreamWas->pDevCfg && pStreamWas->pDevCfg->pIAudioClient)
     1287    {
     1288        hrc = pStreamWas->pDevCfg->pIAudioClient->Stop();
    8331289        LogFunc(("Stop('%s') -> %Rhrc\n", pStreamWas->Cfg.szName, hrc));
    8341290        pStreamWas->fStarted = false;
     
    8371293    if (pStreamWas->cFramesCaptureToRelease)
    8381294    {
    839         hrc = pStreamWas->pIAudioCaptureClient->ReleaseBuffer(0);
     1295        hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->ReleaseBuffer(0);
    8401296        Log4Func(("Releasing capture buffer (%#x frames): %Rhrc\n", pStreamWas->cFramesCaptureToRelease, hrc));
    8411297        pStreamWas->cFramesCaptureToRelease = 0;
     
    8441300    }
    8451301
    846     /* Do IAudioClient first, as it hold references to the capture/render clients. */
    847     uint32_t cClientRefs = 0;
    848     if (pStreamWas->pIAudioClient)
    849     {
    850         cClientRefs = pStreamWas->pIAudioClient->Release();
    851         pStreamWas->pIAudioClient = NULL;
    852     }
    853 
    854     uint32_t cTypeClientRefs = 0;
    855     if (pStreamWas->pIAudioCaptureClient)
    856     {
    857         cTypeClientRefs = pStreamWas->pIAudioCaptureClient->Release();
    858         pStreamWas->pIAudioCaptureClient = NULL;
    859     }
    860 
    861     if (pStreamWas->pIAudioRenderClient)
    862     {
    863         cTypeClientRefs = pStreamWas->pIAudioRenderClient->Release();
    864         pStreamWas->pIAudioRenderClient = NULL;
    865     }
    866 
    867     uint32_t cDevRefs = 0;
    868     if (pStreamWas->pIDevice)
    869     {
    870         cDevRefs = pStreamWas->pIDevice->Release();
    871         pStreamWas->pIDevice = NULL;
    872     }
    873 
    874     RT_NOREF(cDevRefs, cClientRefs, cTypeClientRefs, hrc);
    875     LogFlowFunc(("cDevRefs=%d cClientRefs=%d cTypeClientRefs=%d\n", cDevRefs, cClientRefs, cTypeClientRefs));
     1302    if (pStreamWas->pDevCfg)
     1303    {
     1304        drvHostAudioWasCachePutBack(pThis, pStreamWas->pDevCfg);
     1305        pStreamWas->pDevCfg = NULL;
     1306    }
     1307
     1308    LogFlowFunc(("returns\n"));
    8761309    return VINF_SUCCESS;
    8771310}
     
    8881321static int drvHostAudioWasStreamStartWorker(PDRVHOSTAUDIOWAS pThis, PDRVHOSTAUDIOWASSTREAM pStreamWas, const char *pszOperation)
    8891322{
    890     HRESULT hrc = pStreamWas->pIAudioClient->Start();
     1323    HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Start();
    8911324    LogFlow(("%s: Start(%s) returns %Rhrc\n", pszOperation, pStreamWas->Cfg.szName, hrc));
    8921325    AssertStmt(hrc != AUDCLNT_E_NOT_STOPPED, hrc = S_OK);
     
    9271360    if (pStreamWas->cFramesCaptureToRelease)
    9281361    {
    929         hrc = pStreamWas->pIAudioCaptureClient->ReleaseBuffer(pStreamWas->cFramesCaptureToRelease);
     1362        hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->ReleaseBuffer(pStreamWas->cFramesCaptureToRelease);
    9301363        Log4Func(("Releasing capture buffer (%#x frames): %Rhrc\n", pStreamWas->cFramesCaptureToRelease, hrc));
    9311364        pStreamWas->cFramesCaptureToRelease = 0;
     
    9341367    }
    9351368
    936     hrc = pStreamWas->pIAudioClient->Reset();
     1369    hrc = pStreamWas->pDevCfg->pIAudioClient->Reset();
    9371370    if (FAILED(hrc))
    9381371        LogRelMax(64, ("WasAPI: Stream reset failed when enabling '%s': %Rhrc\n", pStreamWas->Cfg.szName, hrc));
     
    9821415        if (pStreamWas->fStarted)
    9831416        {
    984             HRESULT hrc = pStreamWas->pIAudioClient->Stop();
     1417            HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop();
    9851418            LogFlowFunc(("Stop(%s) returns %Rhrc\n", pStreamWas->Cfg.szName, hrc));
    9861419            if (FAILED(hrc))
     
    10271460        pStreamWas->fRestartOnResume = true;
    10281461
    1029         HRESULT hrc = pStreamWas->pIAudioClient->Stop();
     1462        HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop();
    10301463        LogFlowFunc(("Stop(%s) returns %Rhrc\n", pStreamWas->Cfg.szName, hrc));
    10311464        if (FAILED(hrc))
     
    10901523     */
    10911524    uint64_t        msNext = UINT64_MAX;
    1092     RTCritSectRwEnterShared(&pThis->CritSectList);
     1525    RTCritSectRwEnterShared(&pThis->CritSectStreamList);
    10931526    PDRVHOSTAUDIOWASSTREAM   pCur;
    1094     RTListForEach(&pThis->HeadStreams, pCur, DRVHOSTAUDIOWASSTREAM, ListEntry)
     1527    RTListForEach(&pThis->StreamHead, pCur, DRVHOSTAUDIOWASSTREAM, ListEntry)
    10951528    {
    10961529        if (   pCur->fDraining
     
    11141547                        LogRel2(("WasAPI: Stopping draining of '%s' {%s} ...\n",
    11151548                                 pCur->Cfg.szName, drvHostWasStreamStatusString(pCur)));
    1116                         HRESULT hrc = pCur->pIAudioClient->Stop();
     1549                        HRESULT hrc = pCur->pDevCfg->pIAudioClient->Stop();
    11171550                        if (FAILED(hrc))
    11181551                            LogRelMax(64, ("WasAPI: Failed to stop draining stream '%s': %Rhrc\n", pCur->Cfg.szName, hrc));
     
    11311564    if (msNext != UINT64_MAX)
    11321565        PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hDrainTimer, msNext - msNow);
    1133     RTCritSectRwLeaveShared(&pThis->CritSectList);
     1566    RTCritSectRwLeaveShared(&pThis->CritSectStreamList);
    11341567}
    11351568
     
    11751608                uint64_t       msDrainDeadline = 0;
    11761609                UINT32         cFramesPending  = 0;
    1177                 HRESULT hrc = pStreamWas->pIAudioClient->GetCurrentPadding(&cFramesPending);
     1610                HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->GetCurrentPadding(&cFramesPending);
    11781611                if (SUCCEEDED(hrc))
    11791612                    msDrainDeadline = msNow
     
    12581691    RTCritSectEnter(&pStreamWas->CritSect);
    12591692
    1260     if (pStreamWas->pIAudioCaptureClient /* paranoia */)
     1693    if (pStreamWas->pDevCfg->pIAudioCaptureClient /* paranoia */)
    12611694    {
    12621695        UINT32  cFramesInNextPacket = 0;
    1263         HRESULT hrc = pStreamWas->pIAudioCaptureClient->GetNextPacketSize(&cFramesInNextPacket);
     1696        HRESULT hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->GetNextPacketSize(&cFramesInNextPacket);
    12641697        if (SUCCEEDED(hrc))
    12651698            cbReadable = PDMAudioPropsFramesToBytes(&pStreamWas->Cfg.Props,
     
    12921725
    12931726    if (   pStreamWas->Cfg.enmDir == PDMAUDIODIR_OUT
    1294         && pStreamWas->pIAudioClient /* paranoia */)
     1727        && pStreamWas->pDevCfg->pIAudioClient /* paranoia */)
    12951728    {
    12961729        UINT32  cFramesPending = 0;
    1297         HRESULT hrc = pStreamWas->pIAudioClient->GetCurrentPadding(&cFramesPending);
     1730        HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->GetCurrentPadding(&cFramesPending);
    12981731        if (SUCCEEDED(hrc))
    12991732        {
     
    13351768
    13361769    if (   pStreamWas->Cfg.enmDir == PDMAUDIODIR_OUT
    1337         && pStreamWas->pIAudioClient /* paranoia */)
     1770        && pStreamWas->pDevCfg->pIAudioClient /* paranoia */)
    13381771    {
    13391772        if (pStreamWas->fStarted)
    13401773        {
    13411774            UINT32  cFramesPending = 0;
    1342             HRESULT hrc = pStreamWas->pIAudioClient->GetCurrentPadding(&cFramesPending);
     1775            HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->GetCurrentPadding(&cFramesPending);
    13431776            if (SUCCEEDED(hrc))
    13441777            {
     
    14161849    while (cbBuf > 0)
    14171850    {
    1418         AssertBreakStmt(pStreamWas->pIAudioRenderClient && pStreamWas->pIAudioClient, rc = VERR_AUDIO_STREAM_NOT_READY);
     1851        AssertBreakStmt(pStreamWas->pDevCfg && pStreamWas->pDevCfg->pIAudioRenderClient && pStreamWas->pDevCfg->pIAudioClient,
     1852                        rc = VERR_AUDIO_STREAM_NOT_READY);
    14191853
    14201854        /*
     
    14231857        UINT32   cFramesPending = 0;
    14241858        uint32_t cbWritable = 0;
    1425         HRESULT hrc = pStreamWas->pIAudioClient->GetCurrentPadding(&cFramesPending);
     1859        HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->GetCurrentPadding(&cFramesPending);
    14261860        if (SUCCEEDED(hrc))
    14271861            cbWritable = PDMAudioPropsFramesToBytes(&pStreamWas->Cfg.Props,
     
    14501884         */
    14511885        BYTE *pbData = NULL;
    1452         hrc = pStreamWas->pIAudioRenderClient->GetBuffer(cFramesToWrite, &pbData);
     1886        hrc = pStreamWas->pDevCfg->pIAudioRenderClient->GetBuffer(cFramesToWrite, &pbData);
    14531887        if (SUCCEEDED(hrc))
    14541888        {
    14551889            memcpy(pbData, pvBuf, cbToWrite);
    1456             hrc = pStreamWas->pIAudioRenderClient->ReleaseBuffer(cFramesToWrite, 0 /*fFlags*/);
     1890            hrc = pStreamWas->pDevCfg->pIAudioRenderClient->ReleaseBuffer(cFramesToWrite, 0 /*fFlags*/);
    14571891            if (SUCCEEDED(hrc))
    14581892            {
     
    15591993    while (cbBuf > cbFrame)
    15601994    {
    1561         AssertBreakStmt(pStreamWas->pIAudioCaptureClient && pStreamWas->pIAudioClient, rc = VERR_AUDIO_STREAM_NOT_READY);
     1995        AssertBreakStmt(pStreamWas->pDevCfg->pIAudioCaptureClient && pStreamWas->pDevCfg->pIAudioClient, rc = VERR_AUDIO_STREAM_NOT_READY);
    15621996
    15631997        /*
     
    15772011            if (!pStreamWas->cbCapture)
    15782012            {
    1579                 HRESULT hrc = pStreamWas->pIAudioCaptureClient->ReleaseBuffer(pStreamWas->cFramesCaptureToRelease);
     2013                HRESULT hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->ReleaseBuffer(pStreamWas->cFramesCaptureToRelease);
    15802014                Log4Func(("@%#RX64: Releasing capture buffer (%#x frames): %Rhrc\n",
    15812015                          pStreamWas->offInternal, pStreamWas->cFramesCaptureToRelease, hrc));
     
    16032037         */
    16042038        UINT32 cFramesCaptured = 0;
    1605         HRESULT hrc = pStreamWas->pIAudioCaptureClient->GetNextPacketSize(&cFramesCaptured);
     2039        HRESULT hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->GetNextPacketSize(&cFramesCaptured);
    16062040        if (SUCCEEDED(hrc))
    16072041        {
     
    16262060        DWORD   fBufFlags   = 0;
    16272061        BYTE   *pbData      = NULL;
    1628         hrc = pStreamWas->pIAudioCaptureClient->GetBuffer(&pbData, &cFramesCaptured, &fBufFlags, &offDevice, &uQpsNtTicks);
     2062        hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->GetBuffer(&pbData, &cFramesCaptured, &fBufFlags, &offDevice, &uQpsNtTicks);
    16292063        Log4Func(("@%#RX64: GetBuffer -> %Rhrc pbData=%p cFramesCaptured=%#x fBufFlags=%#x offDevice=%#RX64 uQpcNtTicks=%#RX64\n",
    16302064                  pStreamWas->offInternal, hrc, pbData, cFramesCaptured, fBufFlags, offDevice, uQpsNtTicks));
     
    17092143    }
    17102144
     2145    if (RTCritSectIsInitialized(&pThis->CritSectCache))
     2146    {
     2147        drvHostAudioWasCachePurge(pThis);
     2148        RTCritSectDelete(&pThis->CritSectCache);
     2149    }
     2150
    17112151    if (pThis->pIEnumerator)
    17122152    {
     
    17162156    }
    17172157
    1718     if (RTCritSectRwIsInitialized(&pThis->CritSectList))
    1719         RTCritSectRwDelete(&pThis->CritSectList);
     2158    if (RTCritSectRwIsInitialized(&pThis->CritSectStreamList))
     2159        RTCritSectRwDelete(&pThis->CritSectStreamList);
    17202160
    17212161    LogFlowFuncLeave();
     
    17372177    pThis->pDrvIns                          = pDrvIns;
    17382178    pThis->hDrainTimer                      = NIL_TMTIMERHANDLE;
    1739     RTListInit(&pThis->HeadStreams);
     2179    RTListInit(&pThis->StreamHead);
     2180    RTListInit(&pThis->CacheHead);
    17402181    /* IBase */
    17412182    pDrvIns->IBase.pfnQueryInterface        = drvHostAudioWasQueryInterface;
     
    17652206
    17662207    /*
    1767      * Initialize the critical section early.
    1768      */
    1769     int rc = RTCritSectRwInit(&pThis->CritSectList);
     2208     * Initialize the critical sections early.
     2209     */
     2210    int rc = RTCritSectRwInit(&pThis->CritSectStreamList);
     2211    AssertRCReturn(rc, rc);
     2212
     2213    rc = RTCritSectInit(&pThis->CritSectCache);
    17702214    AssertRCReturn(rc, rc);
    17712215
     
    18312275                                "WasAPI drain", &pThis->hDrainTimer);
    18322276    AssertRCReturn(rc, rc);
     2277
     2278    /*
     2279     * Prime the cache.
     2280     */
     2281    drvHostAudioWasCacheFill(pThis);
    18332282
    18342283    return VINF_SUCCESS;
Note: See TracChangeset for help on using the changeset viewer.

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