Changeset 88663 in vbox for trunk/src/VBox
- Timestamp:
- Apr 22, 2021 6:31:47 PM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioWasApi.cpp
r88630 r88663 28 28 #include <functiondiscoverykeys_devpkey.h> 29 29 #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 30 36 31 37 #include <VBox/vmm/pdmaudioinline.h> … … 52 58 class DrvHostAudioWasMmNotifyClient; 53 59 60 /** Pointer to the cache entry for a host audio device (+dir). */ 61 typedef 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 */ 74 typedef 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. */ 96 typedef DRVHOSTAUDIOWASCACHEDEVCFG *PDRVHOSTAUDIOWASCACHEDEVCFG; 97 98 /** 99 * Per audio device (+ direction) cache entry. 100 */ 101 typedef 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 54 125 /** 55 126 * Data for a WASABI stream. … … 57 128 typedef struct DRVHOSTAUDIOWASSTREAM 58 129 { 59 /** Entry in DRVHOSTAUDIOWAS:: HeadStreams. */60 RTLISTNODE ListEntry;130 /** Entry in DRVHOSTAUDIOWAS::StreamHead. */ 131 RTLISTNODE ListEntry; 61 132 /** 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; 71 136 72 137 /** Set if the stream is enabled. */ 73 bool fEnabled;138 bool fEnabled; 74 139 /** Set if the stream is started (playing/capturing). */ 75 bool fStarted;140 bool fStarted; 76 141 /** Set if the stream is draining (output only). */ 77 bool fDraining;142 bool fDraining; 78 143 /** Set if we should restart the stream on resume (saved pause state). */ 79 bool fRestartOnResume;144 bool fRestartOnResume; 80 145 81 146 /** The RTTimeMilliTS() deadline for the draining of this stream (output). */ 82 uint64_t msDrainDeadline;147 uint64_t msDrainDeadline; 83 148 /** Internal stream offset (bytes). */ 84 uint64_t offInternal;149 uint64_t offInternal; 85 150 /** The RTTimeMilliTS() at the end of the last transfer. */ 86 uint64_t msLastTransfer;151 uint64_t msLastTransfer; 87 152 88 153 /** Input: Current capture buffer (advanced as we read). */ 89 uint8_t *pbCapture;154 uint8_t *pbCapture; 90 155 /** Input: The number of bytes left in the current capture buffer. */ 91 uint32_t cbCapture;156 uint32_t cbCapture; 92 157 /** Input: The full size of what pbCapture is part of (for ReleaseBuffer). */ 93 uint32_t cFramesCaptureToRelease;158 uint32_t cFramesCaptureToRelease; 94 159 95 160 /** Critical section protecting: . */ 96 RTCRITSECT CritSect;161 RTCRITSECT CritSect; 97 162 /** Buffer that drvHostWasStreamStatusString uses. */ 98 char szStatus[128];163 char szStatus[128]; 99 164 } DRVHOSTAUDIOWASSTREAM; 100 165 /** Pointer to a WASABI stream. */ … … 141 206 /** List of streams (DRVHOSTAUDIOWASSTREAM). 142 207 * Requires CritSect ownership. */ 143 RTLISTANCHOR HeadStreams;144 /** Serializing access to HeadStreams. */145 RTCRITSECTRW CritSect List;208 RTLISTANCHOR StreamHead; 209 /** Serializing access to StreamHead. */ 210 RTCRITSECTRW CritSectStreamList; 146 211 147 212 /** Pointer to the MM notification client instance. */ 148 213 DrvHostAudioWasMmNotifyClient *pNotifyClient; 214 /** List of cached devices (DRVHOSTAUDIOWASCACHEDEV). 215 * Protected by CritSectCache */ 216 RTLISTANCHOR CacheHead; 217 /** Serializing access to CacheHead. */ 218 RTCRITSECT CritSectCache; 219 149 220 } DRVHOSTAUDIOWAS; 150 221 /** Pointer to the data for a WASAPI host audio driver instance. */ … … 191 262 pStreamWas->szStatus[off] = '\0'; 192 263 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 */ 278 static 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 */ 302 static 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 */ 341 static 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 */ 377 static 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 */ 404 static 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 */ 426 static PDRVHOSTAUDIOWASCACHEDEVCFG 427 drvHostAudioWasCacheLookupLocked(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 */ 452 static PDRVHOSTAUDIOWASCACHEDEVCFG 453 drvHostAudioWasCacheCreateConfig(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 */ 536 static PDRVHOSTAUDIOWASCACHEDEVCFG 537 drvHostAudioWasCacheLookupOrCreateConfig(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 */ 652 static PDRVHOSTAUDIOWASCACHEDEVCFG 653 drvHostAudioWasCacheLookupOrCreate(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 */ 741 static 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 */ 767 static 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 193 811 } 194 812 … … 533 1151 534 1152 /** 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 not557 * 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 == 8571 || pFmt->wBitsPerSample == 16572 || 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 #endif583 if (PDMAudioPropsAreValid(&pCfg->Props))584 {585 pCfg->Backend.cFramesPreBuffering = pCfg->Backend.cFramesPreBuffering * cFramesBufferSize586 / 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 0593 }594 }595 }596 }597 }598 #endif599 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 /**616 1153 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate} 617 1154 */ … … 642 1179 * Do configuration conversion. 643 1180 */ 644 REFERENCE_TIME const cBufferSizeInNtTicks = PDMAudioPropsFramesToNtTicks(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize);645 1181 WAVEFORMATEX WaveFmtX; 646 1182 drvHostAudioWasWaveFmtExFromCfg(pCfgReq, &WaveFmtX); … … 655 1191 "WasAPI: cBufferSizeInNtTicks = %RU64\n", 656 1192 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) )); 658 1195 659 1196 /* … … 667 1204 else 668 1205 { 1206 /** @todo this takes about 2ms. Cache it and update it using the 1207 * notification client. */ 669 1208 hrc = pThis->pIEnumerator->GetDefaultAudioEndpoint(pCfgReq->enmDir == PDMAUDIODIR_IN ? eCapture : eRender, 670 1209 eMultimedia, &pIDevice); … … 672 1211 } 673 1212 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)); 679 1216 return VERR_AUDIO_STREAM_COULD_NOT_CREATE; 680 1217 } 681 1218 682 1219 /* 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. */ 690 1224 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; 798 1257 } 799 1258 else 800 1259 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);804 1260 805 1261 LogFlowFunc(("returns %Rrc\n", rc)); … … 821 1277 if (RTCritSectIsInitialized(&pStreamWas->CritSect)) 822 1278 { 823 RTCritSectRwEnterExcl(&pThis->CritSect List);1279 RTCritSectRwEnterExcl(&pThis->CritSectStreamList); 824 1280 RTListNodeRemove(&pStreamWas->ListEntry); 825 RTCritSectRwLeaveExcl(&pThis->CritSect List);1281 RTCritSectRwLeaveExcl(&pThis->CritSectStreamList); 826 1282 827 1283 RTCritSectDelete(&pStreamWas->CritSect); 828 1284 } 829 1285 830 if (pStreamWas->fStarted && pStreamWas->p IAudioClient)831 { 832 hrc = pStreamWas->p IAudioClient->Stop();1286 if (pStreamWas->fStarted && pStreamWas->pDevCfg && pStreamWas->pDevCfg->pIAudioClient) 1287 { 1288 hrc = pStreamWas->pDevCfg->pIAudioClient->Stop(); 833 1289 LogFunc(("Stop('%s') -> %Rhrc\n", pStreamWas->Cfg.szName, hrc)); 834 1290 pStreamWas->fStarted = false; … … 837 1293 if (pStreamWas->cFramesCaptureToRelease) 838 1294 { 839 hrc = pStreamWas->p IAudioCaptureClient->ReleaseBuffer(0);1295 hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->ReleaseBuffer(0); 840 1296 Log4Func(("Releasing capture buffer (%#x frames): %Rhrc\n", pStreamWas->cFramesCaptureToRelease, hrc)); 841 1297 pStreamWas->cFramesCaptureToRelease = 0; … … 844 1300 } 845 1301 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")); 876 1309 return VINF_SUCCESS; 877 1310 } … … 888 1321 static int drvHostAudioWasStreamStartWorker(PDRVHOSTAUDIOWAS pThis, PDRVHOSTAUDIOWASSTREAM pStreamWas, const char *pszOperation) 889 1322 { 890 HRESULT hrc = pStreamWas->p IAudioClient->Start();1323 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Start(); 891 1324 LogFlow(("%s: Start(%s) returns %Rhrc\n", pszOperation, pStreamWas->Cfg.szName, hrc)); 892 1325 AssertStmt(hrc != AUDCLNT_E_NOT_STOPPED, hrc = S_OK); … … 927 1360 if (pStreamWas->cFramesCaptureToRelease) 928 1361 { 929 hrc = pStreamWas->p IAudioCaptureClient->ReleaseBuffer(pStreamWas->cFramesCaptureToRelease);1362 hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->ReleaseBuffer(pStreamWas->cFramesCaptureToRelease); 930 1363 Log4Func(("Releasing capture buffer (%#x frames): %Rhrc\n", pStreamWas->cFramesCaptureToRelease, hrc)); 931 1364 pStreamWas->cFramesCaptureToRelease = 0; … … 934 1367 } 935 1368 936 hrc = pStreamWas->p IAudioClient->Reset();1369 hrc = pStreamWas->pDevCfg->pIAudioClient->Reset(); 937 1370 if (FAILED(hrc)) 938 1371 LogRelMax(64, ("WasAPI: Stream reset failed when enabling '%s': %Rhrc\n", pStreamWas->Cfg.szName, hrc)); … … 982 1415 if (pStreamWas->fStarted) 983 1416 { 984 HRESULT hrc = pStreamWas->p IAudioClient->Stop();1417 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop(); 985 1418 LogFlowFunc(("Stop(%s) returns %Rhrc\n", pStreamWas->Cfg.szName, hrc)); 986 1419 if (FAILED(hrc)) … … 1027 1460 pStreamWas->fRestartOnResume = true; 1028 1461 1029 HRESULT hrc = pStreamWas->p IAudioClient->Stop();1462 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop(); 1030 1463 LogFlowFunc(("Stop(%s) returns %Rhrc\n", pStreamWas->Cfg.szName, hrc)); 1031 1464 if (FAILED(hrc)) … … 1090 1523 */ 1091 1524 uint64_t msNext = UINT64_MAX; 1092 RTCritSectRwEnterShared(&pThis->CritSect List);1525 RTCritSectRwEnterShared(&pThis->CritSectStreamList); 1093 1526 PDRVHOSTAUDIOWASSTREAM pCur; 1094 RTListForEach(&pThis-> HeadStreams, pCur, DRVHOSTAUDIOWASSTREAM, ListEntry)1527 RTListForEach(&pThis->StreamHead, pCur, DRVHOSTAUDIOWASSTREAM, ListEntry) 1095 1528 { 1096 1529 if ( pCur->fDraining … … 1114 1547 LogRel2(("WasAPI: Stopping draining of '%s' {%s} ...\n", 1115 1548 pCur->Cfg.szName, drvHostWasStreamStatusString(pCur))); 1116 HRESULT hrc = pCur->p IAudioClient->Stop();1549 HRESULT hrc = pCur->pDevCfg->pIAudioClient->Stop(); 1117 1550 if (FAILED(hrc)) 1118 1551 LogRelMax(64, ("WasAPI: Failed to stop draining stream '%s': %Rhrc\n", pCur->Cfg.szName, hrc)); … … 1131 1564 if (msNext != UINT64_MAX) 1132 1565 PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hDrainTimer, msNext - msNow); 1133 RTCritSectRwLeaveShared(&pThis->CritSect List);1566 RTCritSectRwLeaveShared(&pThis->CritSectStreamList); 1134 1567 } 1135 1568 … … 1175 1608 uint64_t msDrainDeadline = 0; 1176 1609 UINT32 cFramesPending = 0; 1177 HRESULT hrc = pStreamWas->p IAudioClient->GetCurrentPadding(&cFramesPending);1610 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->GetCurrentPadding(&cFramesPending); 1178 1611 if (SUCCEEDED(hrc)) 1179 1612 msDrainDeadline = msNow … … 1258 1691 RTCritSectEnter(&pStreamWas->CritSect); 1259 1692 1260 if (pStreamWas->p IAudioCaptureClient /* paranoia */)1693 if (pStreamWas->pDevCfg->pIAudioCaptureClient /* paranoia */) 1261 1694 { 1262 1695 UINT32 cFramesInNextPacket = 0; 1263 HRESULT hrc = pStreamWas->p IAudioCaptureClient->GetNextPacketSize(&cFramesInNextPacket);1696 HRESULT hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->GetNextPacketSize(&cFramesInNextPacket); 1264 1697 if (SUCCEEDED(hrc)) 1265 1698 cbReadable = PDMAudioPropsFramesToBytes(&pStreamWas->Cfg.Props, … … 1292 1725 1293 1726 if ( pStreamWas->Cfg.enmDir == PDMAUDIODIR_OUT 1294 && pStreamWas->p IAudioClient /* paranoia */)1727 && pStreamWas->pDevCfg->pIAudioClient /* paranoia */) 1295 1728 { 1296 1729 UINT32 cFramesPending = 0; 1297 HRESULT hrc = pStreamWas->p IAudioClient->GetCurrentPadding(&cFramesPending);1730 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->GetCurrentPadding(&cFramesPending); 1298 1731 if (SUCCEEDED(hrc)) 1299 1732 { … … 1335 1768 1336 1769 if ( pStreamWas->Cfg.enmDir == PDMAUDIODIR_OUT 1337 && pStreamWas->p IAudioClient /* paranoia */)1770 && pStreamWas->pDevCfg->pIAudioClient /* paranoia */) 1338 1771 { 1339 1772 if (pStreamWas->fStarted) 1340 1773 { 1341 1774 UINT32 cFramesPending = 0; 1342 HRESULT hrc = pStreamWas->p IAudioClient->GetCurrentPadding(&cFramesPending);1775 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->GetCurrentPadding(&cFramesPending); 1343 1776 if (SUCCEEDED(hrc)) 1344 1777 { … … 1416 1849 while (cbBuf > 0) 1417 1850 { 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); 1419 1853 1420 1854 /* … … 1423 1857 UINT32 cFramesPending = 0; 1424 1858 uint32_t cbWritable = 0; 1425 HRESULT hrc = pStreamWas->p IAudioClient->GetCurrentPadding(&cFramesPending);1859 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->GetCurrentPadding(&cFramesPending); 1426 1860 if (SUCCEEDED(hrc)) 1427 1861 cbWritable = PDMAudioPropsFramesToBytes(&pStreamWas->Cfg.Props, … … 1450 1884 */ 1451 1885 BYTE *pbData = NULL; 1452 hrc = pStreamWas->p IAudioRenderClient->GetBuffer(cFramesToWrite, &pbData);1886 hrc = pStreamWas->pDevCfg->pIAudioRenderClient->GetBuffer(cFramesToWrite, &pbData); 1453 1887 if (SUCCEEDED(hrc)) 1454 1888 { 1455 1889 memcpy(pbData, pvBuf, cbToWrite); 1456 hrc = pStreamWas->p IAudioRenderClient->ReleaseBuffer(cFramesToWrite, 0 /*fFlags*/);1890 hrc = pStreamWas->pDevCfg->pIAudioRenderClient->ReleaseBuffer(cFramesToWrite, 0 /*fFlags*/); 1457 1891 if (SUCCEEDED(hrc)) 1458 1892 { … … 1559 1993 while (cbBuf > cbFrame) 1560 1994 { 1561 AssertBreakStmt(pStreamWas->p IAudioCaptureClient && pStreamWas->pIAudioClient, rc = VERR_AUDIO_STREAM_NOT_READY);1995 AssertBreakStmt(pStreamWas->pDevCfg->pIAudioCaptureClient && pStreamWas->pDevCfg->pIAudioClient, rc = VERR_AUDIO_STREAM_NOT_READY); 1562 1996 1563 1997 /* … … 1577 2011 if (!pStreamWas->cbCapture) 1578 2012 { 1579 HRESULT hrc = pStreamWas->p IAudioCaptureClient->ReleaseBuffer(pStreamWas->cFramesCaptureToRelease);2013 HRESULT hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->ReleaseBuffer(pStreamWas->cFramesCaptureToRelease); 1580 2014 Log4Func(("@%#RX64: Releasing capture buffer (%#x frames): %Rhrc\n", 1581 2015 pStreamWas->offInternal, pStreamWas->cFramesCaptureToRelease, hrc)); … … 1603 2037 */ 1604 2038 UINT32 cFramesCaptured = 0; 1605 HRESULT hrc = pStreamWas->p IAudioCaptureClient->GetNextPacketSize(&cFramesCaptured);2039 HRESULT hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->GetNextPacketSize(&cFramesCaptured); 1606 2040 if (SUCCEEDED(hrc)) 1607 2041 { … … 1626 2060 DWORD fBufFlags = 0; 1627 2061 BYTE *pbData = NULL; 1628 hrc = pStreamWas->p IAudioCaptureClient->GetBuffer(&pbData, &cFramesCaptured, &fBufFlags, &offDevice, &uQpsNtTicks);2062 hrc = pStreamWas->pDevCfg->pIAudioCaptureClient->GetBuffer(&pbData, &cFramesCaptured, &fBufFlags, &offDevice, &uQpsNtTicks); 1629 2063 Log4Func(("@%#RX64: GetBuffer -> %Rhrc pbData=%p cFramesCaptured=%#x fBufFlags=%#x offDevice=%#RX64 uQpcNtTicks=%#RX64\n", 1630 2064 pStreamWas->offInternal, hrc, pbData, cFramesCaptured, fBufFlags, offDevice, uQpsNtTicks)); … … 1709 2143 } 1710 2144 2145 if (RTCritSectIsInitialized(&pThis->CritSectCache)) 2146 { 2147 drvHostAudioWasCachePurge(pThis); 2148 RTCritSectDelete(&pThis->CritSectCache); 2149 } 2150 1711 2151 if (pThis->pIEnumerator) 1712 2152 { … … 1716 2156 } 1717 2157 1718 if (RTCritSectRwIsInitialized(&pThis->CritSect List))1719 RTCritSectRwDelete(&pThis->CritSect List);2158 if (RTCritSectRwIsInitialized(&pThis->CritSectStreamList)) 2159 RTCritSectRwDelete(&pThis->CritSectStreamList); 1720 2160 1721 2161 LogFlowFuncLeave(); … … 1737 2177 pThis->pDrvIns = pDrvIns; 1738 2178 pThis->hDrainTimer = NIL_TMTIMERHANDLE; 1739 RTListInit(&pThis->HeadStreams); 2179 RTListInit(&pThis->StreamHead); 2180 RTListInit(&pThis->CacheHead); 1740 2181 /* IBase */ 1741 2182 pDrvIns->IBase.pfnQueryInterface = drvHostAudioWasQueryInterface; … … 1765 2206 1766 2207 /* 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); 1770 2214 AssertRCReturn(rc, rc); 1771 2215 … … 1831 2275 "WasAPI drain", &pThis->hDrainTimer); 1832 2276 AssertRCReturn(rc, rc); 2277 2278 /* 2279 * Prime the cache. 2280 */ 2281 drvHostAudioWasCacheFill(pThis); 1833 2282 1834 2283 return VINF_SUCCESS;
Note:
See TracChangeset
for help on using the changeset viewer.