Changeset 87964 in vbox
- Timestamp:
- Mar 4, 2021 11:34:43 PM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 143067
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/HDAStream.cpp
r87962 r87964 357 357 358 358 /* Figure out how many transfer fragments we're going to use for this stream. */ 359 uint 8_t cTransferFragments = pStreamShared->u16LVI + 1;359 uint32_t cTransferFragments = pStreamShared->u16LVI + 1; 360 360 if (cTransferFragments <= 1) 361 LogRel(("HDA: Warning: Stream #%RU8 transfer fragments (%RU 8) invalid -- buggy guest audio driver!\n",361 LogRel(("HDA: Warning: Stream #%RU8 transfer fragments (%RU16) invalid -- buggy guest audio driver!\n", 362 362 uSD, pStreamShared->u16LVI)); 363 363 … … 426 426 /* Initialize position adjustment counter. */ 427 427 pStreamShared->State.cfPosAdjustDefault = cfPosAdjust; 428 pStreamShared->State.cfPosAdjustLeft = pStreamShared->State.cfPosAdjustDefault; 429 430 LogRel2(("HDA: Position adjustment for stream #%RU8 active (%RU32 frames)\n", 431 uSD, pStreamShared->State.cfPosAdjustDefault)); 432 } 433 } 434 435 Log3Func(("[SD%RU8] cfPosAdjust=%RU32, cFragments=%RU8\n", uSD, cfPosAdjust, cTransferFragments)); 428 pStreamShared->State.cfPosAdjustLeft = cfPosAdjust; 429 LogRel2(("HDA: Position adjustment for stream #%RU8 active (%RU32 frames)\n", uSD, cfPosAdjust)); 430 } 431 } 432 433 Log3Func(("[SD%RU8] cfPosAdjust=%RU32, cFragments=%RU32\n", uSD, cfPosAdjust, cTransferFragments)); 436 434 437 435 /* … … 460 458 const uint32_t cbDataPerSec = DrvAudioHlpMilliToBytes(RT_MS_1SEC, &pStreamR3->State.Mapping.PCMProps); 461 459 460 /* This is used to indicate whether we're done or should the uTimerIoHz as fallback. */ 461 rc = VINF_SUCCESS; 462 462 463 /* The transfer Hz depend on the heuristics above, that is, 463 464 how often the guest expects to see a new data transfer. */ 464 uint16_t uTransferHz = 0; 465 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.uTimerIoHz, 466 ("I/O timer Hz rate for stream #%RU8 is invalid\n", uSD), 467 pStreamShared->State.uTimerIoHz = HDA_TIMER_HZ_DEFAULT); 468 unsigned uTransferHz = pStreamShared->State.uTimerIoHz; 465 469 466 470 LogRel2(("HDA: Stream #%RU8 needs %RU32 bytes/s audio data\n", uSD, cbDataPerSec)); … … 468 472 if (pThis->fTransferHeuristicsEnabled) /* Are data transfer heuristics enabled? */ 469 473 { 470 471 /* Use the whole CBL as a starting point.472 * This basically ASSUMES that we have one consequtive buffer with only one interrupt at the end. */473 uint32_t cbTransferHeuristicsMin = pStreamShared->u32CBL;474 475 474 /* Don't take frames (as bytes) into account which are part of the position adjustment. */ 476 475 uint32_t cbTransferHeuristicsPosAdjust = pStreamShared->State.cfPosAdjustDefault * pStreamR3->State.Mapping.cbFrameSize; 477 478 HDABDLEDESC bd; 479 uint32_t cbTransferHeuristicsCur = 0; 480 for (uint8_t i = 0; i < cTransferFragments; i++) 481 { 476 uint32_t cbTransferHeuristics = 0; 477 uint32_t cbTransferHeuristicsCur = 0; 478 uint32_t cBufferIrqs = 0; 479 for (uint32_t i = 0; i < cTransferFragments; i++) 480 { 481 /** @todo wrong read type! */ 482 HDABDLEDESC bd = { 0, 0, 0 }; 482 483 PDMDevHlpPhysRead(pDevIns, u64BDLBase + i * sizeof(HDABDLEDESC), &bd, sizeof(bd)); 483 484 … … 501 502 { 502 503 cbTransferHeuristicsCur += bd.u32BufSize; 503 cbTransferHeuristicsMin = RT_MIN(cbTransferHeuristicsCur, cbTransferHeuristicsMin); 504 cbTransferHeuristicsCur = 0; 504 if ( cbTransferHeuristicsCur == cbTransferHeuristics 505 || !cbTransferHeuristics) 506 cbTransferHeuristics = cbTransferHeuristicsCur; 507 else 508 { 509 /** @todo r=bird: you need to find the smallest common denominator here, not 510 * just the minimum. Ignoring this for now as windows has two equal 511 * sized buffers both with IOC set. */ 512 LogRel(("HDA: Uneven IRQ buffer config; i=%u cbCur=%#x cbMin=%#x.\n", i, cbTransferHeuristicsCur, cbTransferHeuristics)); 513 cbTransferHeuristics = RT_MIN(cbTransferHeuristicsCur, cbTransferHeuristics); 514 } 515 cbTransferHeuristicsCur = 0; 516 cBufferIrqs++; 505 517 } 506 518 else /* No interrupt expected -> add it to the former BDLE size. */ … … 508 520 } 509 521 510 /* !!! HACK ALERT BEGIN !!! */ 511 512 /* Windows 10's audio driver expects a transfer all ~10.1ms (~1764 bytes), although 513 * it sets up multiples of 1792 bytes (896 bytes per channel) per BDLE: 522 /* 523 * IFF the guest is using buffer IRQ, configure the timer to 524 * the lowest common denominator of those IRQ periods. 514 525 * 515 * * 2 (stereo) channels = 1792 516 * * 6 (surrounnd) channels = 1792 * 3 = 5376 517 * 518 * Same seems to apply to Windows 7 when using the default setup. 519 * 520 * I currently don't have any clue why it does this that way, so try to simply detect this 521 * and alter the value so that we get a somewhat proper audio output. 522 * Probably alignment? 526 * Otherwise, fall back on using default timer I/O Hz (set above). 523 527 */ 524 if (cbTransferHeuristicsMin / (1792 / 2 /* Channels */) == pStreamR3->State.Mapping.PCMProps.cChannels) 525 { 526 LogRel2(("HDA: Heuristics detected a Windows guest -- setting a fixed transfer minimum size\n")); 527 cbTransferHeuristicsMin = 1764; 528 529 /* Anything above 5 channels (surround) will need higher timer rates -- otherwise it will be a lot worse. */ 530 if (pStreamR3->State.Mapping.PCMProps.cChannels >= 5) 531 { 532 uTransferHz = 100 + ((pStreamR3->State.Mapping.PCMProps.cChannels - 4) * 50); /* Add 50Hz per additional channel. */ 533 } 534 else /* Stick with 100Hz here. Anything lower will crackle. */ 535 uTransferHz = 100; 536 537 pStreamShared->State.uTimerIoHz = 50; 538 } 539 540 /* !!! HACK ALERT END !!! */ 541 542 else 543 { 544 /* Calculate the transfer Hz rate by dividing the guessed transfer data rate by the data rate in ms. */ 545 uTransferHz = cbTransferHeuristicsMin / (cbDataPerSec / RT_MS_1SEC); 546 547 /* Make sure that the timer I/O Hz rate is the same in this case, as we can't do any more guessing here. */ 548 pStreamShared->State.uTimerIoHz = uTransferHz; 549 } 550 551 LogRel2(("HDA: Stream #%RU8 seems to need a transfer worth of %RU32 bytes audio data, so trying to run transfers at %uHz\n", 552 uSD, cbTransferHeuristicsMin, uTransferHz)); 553 } 554 else /* Heuristics disabled. */ 555 { 556 /* Use global timing rate instead. */ 557 uTransferHz = pThis->uTimerHz; 528 if (cBufferIrqs > 0 && cbTransferHeuristics > 1) 529 { 530 pStreamShared->State.cbTransferSize = cbTransferHeuristics; 531 pStreamShared->State.cbTransferChunk = cbTransferHeuristics; /* no chunking */ 532 ASSERT_GUEST_LOGREL_MSG(DrvAudioHlpBytesIsAligned(cbTransferHeuristics, &pCfg->Props), 533 ("We arrived at a misaligned transfer size for stream #%RU8: %#x (%u)\n", 534 uSD, cbTransferHeuristics, cbTransferHeuristics)); 535 536 /* Convert to timer ticks. */ 537 uint64_t const cTimerTicksPerSec = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer); 538 uint64_t const cbTransferPerSec = RT_MAX(pStreamShared->State.Cfg.Props.uHz * pStreamR3->State.Mapping.cbFrameSize, 539 4096 /* zero div prevention: min is 6kHz, picked 4k in case I'm mistaken */); 540 pStreamShared->State.cTicksPerByte = (cTimerTicksPerSec + cbTransferPerSec / 2) / cbTransferPerSec; 541 AssertStmt(pStreamShared->State.cTicksPerByte, pStreamShared->State.cTicksPerByte = 4096); 542 543 pStreamShared->State.cTransferTicks = (cTimerTicksPerSec * cbTransferHeuristics + cbTransferPerSec / 2) 544 / cbTransferPerSec; 545 546 /* Estimate timer HZ for the circular buffer setup. */ 547 uTransferHz = cbTransferPerSec * 1000 / cbTransferHeuristics; 548 LogRel2(("HDA: Stream #%RU8 needs a data transfer at least every %RU64 ticks / %RU32 bytes / approx %u.%03u Hz\n", 549 uSD, pStreamShared->State.cTransferTicks, cbTransferHeuristics, uTransferHz / 1000, uTransferHz % 1000)); 550 uTransferHz /= 1000; 551 552 /* Indicate that we're done with period calculation. */ 553 rc = VINF_ALREADY_INITIALIZED; 554 } 558 555 } 559 556 560 557 if (uTransferHz > 400) /* Anything above 400 Hz looks fishy -- tell the user. */ 561 LogRel(("HDA: Warning: Calculated transfer Hz rate for stream #%RU8 looks incorrect (%u), " 562 "please re-run with audio debug mode and report a bug\n", 558 LogRel(("HDA: Warning: Calculated transfer Hz rate for stream #%RU8 looks incorrect (%u), please re-run with audio debug mode and report a bug\n", 563 559 uSD, uTransferHz)); 564 565 LogRel2(("HDA: Stream #%RU8 transfer timer rate is %RU16Hz, I/O timer rate is %RU16Hz\n",566 uSD, uTransferHz, pStreamShared->State.uTimerIoHz));567 568 /* Make sure that the chosen transfer Hz rate dividable by the stream's overall data rate. */569 ASSERT_GUEST_LOGREL_MSG_STMT(cbDataPerSec % uTransferHz == 0,570 ("Transfer data rate (%RU32 bytes/s) for stream #%RU8 does not fit to stream timing (%RU32Hz)\n",571 cbDataPerSec, uSD, uTransferHz),572 uTransferHz = HDA_TIMER_HZ_DEFAULT);573 574 /* Prevent division by zero. */575 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.uTimerIoHz,576 ("I/O timer Hz rate for stream #%RU8 is invalid\n", uSD),577 pStreamShared->State.uTimerIoHz = HDA_TIMER_HZ_DEFAULT);578 560 579 561 /* Set I/O scheduling hint for the backends. */ … … 581 563 LogRel2(("HDA: Stream #%RU8 set scheduling hint for the backends to %RU32ms\n", uSD, pCfg->Device.cMsSchedulingHint)); 582 564 583 pStreamShared->State.cbTransferSize = 584 (pStreamR3->State.Mapping.PCMProps.uHz * pStreamR3->State.Mapping.cbFrameSize) / 100 /* Hz */; 585 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferSize, 586 ("Transfer size for stream #%RU8 is invalid\n", uSD), rc = VERR_INVALID_PARAMETER); 565 if (rc != VINF_ALREADY_INITIALIZED && RT_SUCCESS(rc)) 566 { 567 /* 568 * Transfer heuristics disabled or failed. 569 */ 570 Assert(uTransferHz == pStreamShared->State.uTimerIoHz); 571 LogRel2(("HDA: Stream #%RU8 transfer timer and I/O timer rate is %u Hz.\n", uSD, uTransferHz)); 572 573 /* Make sure that the chosen transfer Hz rate dividable by the stream's overall data rate. */ 574 ASSERT_GUEST_LOGREL_MSG_STMT(cbDataPerSec % uTransferHz == 0, 575 ("Transfer data rate (%RU32 bytes/s) for stream #%RU8 does not fit to stream timing (%u Hz)\n", 576 cbDataPerSec, uSD, uTransferHz), 577 uTransferHz = HDA_TIMER_HZ_DEFAULT); 578 579 pStreamShared->State.cbTransferSize = (pStreamR3->State.Mapping.PCMProps.uHz * pStreamR3->State.Mapping.cbFrameSize) 580 / uTransferHz; 581 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferSize, 582 ("Transfer size for stream #%RU8 is invalid\n", uSD), rc = VERR_INVALID_PARAMETER); 583 if (RT_SUCCESS(rc)) 584 { 585 /* 586 * Calculate the bytes we need to transfer to / from the stream's DMA per iteration. 587 * This is bound to the device's Hz rate and thus to the (virtual) timing the device expects. 588 * 589 * As we don't do chunked transfers the moment, the chunk size equals the overall transfer size. 590 */ 591 pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize; 592 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferChunk, 593 ("Transfer chunk for stream #%RU8 is invalid\n", uSD), 594 rc = VERR_INVALID_PARAMETER); 595 if (RT_SUCCESS(rc)) 596 { 597 /* Make sure that the transfer chunk does not exceed the overall transfer size. */ 598 AssertStmt(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize, 599 pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize); 600 601 const uint64_t uTimerFreq = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer); 602 603 const double cTicksPerHz = uTimerFreq / uTransferHz; 604 605 double cTicksPerByte = cTicksPerHz / (double)pStreamShared->State.cbTransferChunk; 606 if (uTransferHz < 100) 607 cTicksPerByte /= 100 / uTransferHz; 608 else 609 cTicksPerByte *= uTransferHz / 100; 610 Assert(cTicksPerByte); 611 612 #define HDA_ROUND_NEAREST(a_X) ((a_X) >= 0 ? (uint32_t)((a_X) + 0.5) : (uint32_t)((a_X) - 0.5)) 613 614 /* Calculate the timer ticks per byte for this stream. */ 615 pStreamShared->State.cTicksPerByte = HDA_ROUND_NEAREST(cTicksPerByte); 616 Assert(pStreamShared->State.cTicksPerByte); 617 618 const double cTransferTicks = pStreamShared->State.cbTransferChunk * cTicksPerByte; 619 620 /* Calculate timer ticks per transfer. */ 621 pStreamShared->State.cTransferTicks = HDA_ROUND_NEAREST(cTransferTicks); 622 Assert(pStreamShared->State.cTransferTicks); 623 #undef HDA_ROUND_NEAREST 624 625 LogRel2(("HDA: Stream #%RU8 is using %uHz I/O timer (%RU64 virtual ticks / Hz), stream Hz=%RU32, cTicksPerByte=%RU64, cTransferTicks=%RU64 -> cbTransferChunk=%RU32 (%RU64ms), cbTransferSize=%RU32 (%RU64ms)\n", 626 uSD, pStreamShared->State.uTimerIoHz, (uint64_t)cTicksPerHz, pStreamR3->State.Mapping.PCMProps.uHz, 627 pStreamShared->State.cTicksPerByte, pStreamShared->State.cTransferTicks, 628 pStreamShared->State.cbTransferChunk, DrvAudioHlpBytesToMilli(pStreamShared->State.cbTransferChunk, &pStreamR3->State.Mapping.PCMProps), 629 pStreamShared->State.cbTransferSize, DrvAudioHlpBytesToMilli(pStreamShared->State.cbTransferSize, &pStreamR3->State.Mapping.PCMProps))); 630 } 631 } 632 } 587 633 588 634 if (RT_SUCCESS(rc)) 589 635 { 636 /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */ 637 hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD)); 638 639 #ifdef LOG_ENABLED 640 hdaR3BDLEDumpAll(pDevIns, pThis, pStreamShared->u64BDLBase, pStreamShared->u16LVI + 1); 641 #endif 642 590 643 /* 591 * Calculate the bytes we need to transfer to / from the stream's DMA per iteration. 592 * This is bound to the device's Hz rate and thus to the (virtual) timing the device expects. 593 * 594 * As we don't do chunked transfers the moment, the chunk size equals the overall transfer size. 644 * Set up internal ring buffer. 595 645 */ 596 pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize; 597 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferChunk, 598 ("Transfer chunk for stream #%RU8 is invalid\n", uSD), 599 rc = VERR_INVALID_PARAMETER); 600 if (RT_SUCCESS(rc)) 601 { 602 /* Make sure that the transfer chunk does not exceed the overall transfer size. */ 603 AssertStmt(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize, 604 pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize); 605 606 const uint64_t uTimerFreq = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer); 607 608 const double cTicksPerHz = uTimerFreq / uTransferHz; 609 double cTicksPerByte = cTicksPerHz / (double)pStreamShared->State.cbTransferChunk; 610 611 if (uTransferHz < 100) 612 cTicksPerByte /= 100 / uTransferHz; 613 else 614 cTicksPerByte *= uTransferHz / 100; 615 616 Assert(cTicksPerByte); 617 618 #define HDA_ROUND_NEAREST(a_X) ((a_X) >= 0 ? (uint32_t)((a_X) + 0.5) : (uint32_t)((a_X) - 0.5)) /** @todo r=andy Do we have rounding in IPRT? */ 619 620 /* Calculate the timer ticks per byte for this stream. */ 621 pStreamShared->State.cTicksPerByte = HDA_ROUND_NEAREST(cTicksPerByte); 622 Assert(pStreamShared->State.cTicksPerByte); 623 624 const double cTransferTicks = pStreamShared->State.cbTransferChunk * cTicksPerByte; 625 626 /* Calculate timer ticks per transfer. */ 627 pStreamShared->State.cTransferTicks = HDA_ROUND_NEAREST(cTransferTicks); 628 Assert(pStreamShared->State.cTransferTicks); 629 630 #undef HDA_ROUND_NEAREST 631 632 LogRel2(("HDA: Stream #%RU8 is using %uHz I/O timer (%RU64 virtual ticks / Hz), stream Hz=%RU32, " 633 "cTicksPerByte=%RU64, cTransferTicks=%RU64 -> cbTransferChunk=%RU32 (%RU64ms), cbTransferSize=%RU32 (%RU64ms)\n", 634 uSD, pStreamShared->State.uTimerIoHz, (uint64_t)cTicksPerHz, pStreamR3->State.Mapping.PCMProps.uHz, 635 pStreamShared->State.cTicksPerByte, pStreamShared->State.cTransferTicks, 636 pStreamShared->State.cbTransferChunk, DrvAudioHlpBytesToMilli(pStreamShared->State.cbTransferChunk, &pStreamR3->State.Mapping.PCMProps), 637 pStreamShared->State.cbTransferSize, DrvAudioHlpBytesToMilli(pStreamShared->State.cbTransferSize, &pStreamR3->State.Mapping.PCMProps))); 638 639 /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */ 640 hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD)); 641 642 #ifdef LOG_ENABLED 643 hdaR3BDLEDumpAll(pDevIns, pThis, pStreamShared->u64BDLBase, pStreamShared->u16LVI + 1); 644 #endif 645 } 646 } 647 648 /* 649 * Set up internal ring buffer. 650 */ 651 if (RT_SUCCESS(rc)) 652 { 646 653 647 /* (Re-)Allocate the stream's internal DMA buffer, 654 648 * based on the timing *and* PCM properties we just got above. */ … … 667 661 * We always use triple the minimum timing of both timings for safety (triple buffering), 668 662 * otherwise we risk running into buffer overflows. 663 * 664 * Note: Use pCfg->Props as PCM properties here, as we only want to store the samples we actually need, 665 * in other words, skipping the interleaved channels we don't support / need to save space. 669 666 */ 670 const unsigned uTransferHzMin = RT_MIN(uTransferHz, pStreamShared->State.uTimerIoHz); 671 672 /* Note: Use pCfg->Props as PCM properties here, as we only want to store the samples we actually need, 673 * in other words, skipping the interleaved channels we don't support / need to save space. */ 674 675 uint32_t cbCircBuf; 667 668 const unsigned uTransferHzMin = RT_MIN(uTransferHz, pStreamShared->State.uTimerIoHz); 676 669 const uint32_t cbCircBufDefault = DrvAudioHlpMilliToBytes((RT_MS_1SEC / uTransferHzMin) * 3, &pCfg->Props); 677 670 … … 679 672 uSD, DrvAudioHlpBytesToMilli(cbCircBufDefault, &pCfg->Props), cbCircBufDefault)); 680 673 674 uint32_t cbCircBuf; 681 675 uint32_t cbCircBufGlobal = DrvAudioHlpMilliToBytes( hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN 682 676 ? pThis->cbCircBufInMs : pThis->cbCircBufOutMs, &pCfg->Props); … … 695 689 cbCircBuf, uSD, pCfg->Props.cbSample * pCfg->Props.cChannels), 696 690 rc = VERR_INVALID_PARAMETER); 697 698 ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf, 699 ("Ring buffer size for stream #%RU8 is invalid\n", uSD), 691 ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf, ("Ring buffer size for stream #%RU8 is invalid\n", uSD), 700 692 rc = VERR_INVALID_PARAMETER); 701 693 if (RT_SUCCESS(rc)) … … 707 699 708 700 #ifdef VBOX_WITH_DTRACE 709 VBOXDD_HDA_STREAM_SETUP((uint32_t)uSD, rc, /** @todo uHz - 44100 and such */ 0,701 VBOXDD_HDA_STREAM_SETUP((uint32_t)uSD, rc, pStreamShared->State.Cfg.Props.uHz, 710 702 pStreamShared->State.cTransferTicks, pStreamShared->State.cbTransferSize); 711 703 #endif
Note:
See TracChangeset
for help on using the changeset viewer.