Changeset 88991 in vbox for trunk/src/VBox/Devices/Audio
- Timestamp:
- May 12, 2021 12:46:35 AM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 144329
- Location:
- trunk/src/VBox/Devices/Audio
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/AudioMixer.cpp
r88957 r88991 105 105 static int audioMixerSinkInit(PAUDMIXSINK pSink, PAUDIOMIXER pMixer, const char *pcszName, PDMAUDIODIR enmDir, PPDMDEVINS pDevIns); 106 106 static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink, PPDMDEVINS pDevIns); 107 static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster);107 static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, PCPDMAUDIOVOLUME pVolMaster); 108 108 static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink); 109 109 static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream); … … 117 117 118 118 /** size of output buffer for dbgAudioMixerSinkStatusToStr. */ 119 #define AUDIOMIXERSINK_STATUS_STR_MAX sizeof("RUNNING PENDING_DISABLEDIRTY 0x12345678")119 #define AUDIOMIXERSINK_STATUS_STR_MAX sizeof("RUNNING DRAINING DRAINED_DMA DRAINED_MIXBUF DIRTY 0x12345678") 120 120 121 121 /** … … 138 138 } s_aFlags[] = 139 139 { 140 { RT_STR_TUPLE("RUNNING "), AUDMIXSINK_STS_RUNNING }, 141 { RT_STR_TUPLE("PENDING_DISABLE "), AUDMIXSINK_STS_PENDING_DISABLE }, 142 { RT_STR_TUPLE("DIRTY "), AUDMIXSINK_STS_DIRTY }, 140 { RT_STR_TUPLE("RUNNING "), AUDMIXSINK_STS_RUNNING }, 141 { RT_STR_TUPLE("DRAINING "), AUDMIXSINK_STS_DRAINING }, 142 { RT_STR_TUPLE("DRAINED_DMA"), AUDMIXSINK_STS_DRAINED_DMA }, 143 { RT_STR_TUPLE("DRAINED_MIXBUF"), AUDMIXSINK_STS_DRAINED_MIXBUF }, 144 { RT_STR_TUPLE("DIRTY "), AUDMIXSINK_STS_DIRTY }, 143 145 }; 144 146 char *psz = pszDst; … … 248 250 249 251 pMixer->fFlags = fFlags; 252 pMixer->uMagic = AUDIOMIXER_MAGIC; 250 253 251 254 if (pMixer->fFlags & AUDMIXER_FLAGS_DEBUG) … … 282 285 { 283 286 RT_NOREF(pszArgs); 287 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC); 288 289 int rc2 = RTCritSectEnter(&pMixer->CritSect); 290 AssertRCReturnVoid(rc2); 291 292 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName, 293 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted); 294 284 295 PAUDMIXSINK pSink; 285 296 unsigned iSink = 0; 286 287 int rc2 = RTCritSectEnter(&pMixer->CritSect);288 if (RT_FAILURE(rc2))289 return;290 291 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,292 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);293 294 297 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node) 295 298 { … … 313 316 if (!pMixer) 314 317 return; 318 AssertPtr(pMixer); 319 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC); 315 320 316 321 int rc2 = RTCritSectEnter(&pMixer->CritSect); … … 318 323 319 324 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName)); 325 pMixer->uMagic = AUDIOMIXER_MAGIC_DEAD; 320 326 321 327 PAUDMIXSINK pSink, pSinkNext; … … 347 353 * @param pMixer Mixer to invalidate data for. 348 354 */ 349 int audioMixerInvalidateInternal(PAUDIOMIXER pMixer)355 static int audioMixerInvalidateInternal(PAUDIOMIXER pMixer) 350 356 { 351 357 AssertPtrReturn(pMixer, VERR_INVALID_POINTER); 352 353 358 LogFlowFunc(("[%s]\n", pMixer->pszName)); 354 359 … … 374 379 { 375 380 AssertPtrReturnVoid(pMixer); 381 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC); 376 382 377 383 int rc2 = RTCritSectEnter(&pMixer->CritSect); … … 453 459 { 454 460 AssertPtrReturn(pMixer, VERR_INVALID_POINTER); 461 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC); 455 462 AssertPtrReturn(pVol, VERR_INVALID_POINTER); 456 463 457 464 int rc = RTCritSectEnter(&pMixer->CritSect); 458 if (RT_FAILURE(rc)) 459 return rc; 465 AssertRCReturn(rc, rc); 460 466 461 467 memcpy(&pMixer->VolMaster, pVol, sizeof(PDMAUDIOVOLUME)); … … 478 484 *********************************************************************************************************************************/ 479 485 486 #ifdef VBOX_STRICT 487 /** 488 * Checks if @a pNeedle is in the list of streams associated with @a pSink. 489 * @returns true / false. 490 */ 491 static bool audioMixerSinkIsStreamInList(PAUDMIXSINK pSink, PAUDMIXSTREAM pNeedle) 492 { 493 PAUDMIXSTREAM pStream; 494 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node) 495 { 496 if (pStream == pNeedle) 497 return true; 498 } 499 return false; 500 } 501 #endif /* VBOX_STRICT */ 502 480 503 /** 481 504 * Adds an audio stream to a specific audio sink. … … 489 512 LogFlowFuncEnter(); 490 513 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 514 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 491 515 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 516 Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC); 492 517 AssertPtrReturn(pStream->pConn, VERR_AUDIO_STREAM_NOT_READY); 493 518 AssertReturn(pStream->pSink == NULL, VERR_ALREADY_EXISTS); … … 504 529 */ 505 530 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING) 506 && !(pSink->fStatus & AUDMIXSINK_STS_ PENDING_DISABLE))531 && !(pSink->fStatus & AUDMIXSINK_STS_DRAINING)) 507 532 { 508 533 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE); … … 544 569 { 545 570 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 571 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 546 572 AssertPtrReturn(pConn, VERR_INVALID_POINTER); 547 573 AssertPtrReturn(pCfg, VERR_INVALID_POINTER); 548 574 AssertPtrNullReturn(ppStream, VERR_INVALID_POINTER); 549 RT_NOREF(pDevIns); /* we'll probably be adding more statistics */575 Assert(pSink->AIO.pDevIns == pDevIns); RT_NOREF(pDevIns); /* we'll probably be adding more statistics */ 550 576 551 577 /* … … 623 649 if (RT_SUCCESS(rc)) 624 650 { 651 pMixStream->cFramesBackendBuffer = CfgHost.Backend.cFramesBufferSize; 652 625 653 /* Set up the mixing buffer conversion state. */ 626 654 if (pSink->enmDir == PDMAUDIODIR_OUT) … … 632 660 633 661 /* Increase the stream's reference count to let others know 634 * we're re yling on it to be around now. */662 * we're relying on it to be around now. */ 635 663 pConn->pfnStreamRetain(pConn, pStream); 636 pMixStream->pConn = pConn; 664 pMixStream->pConn = pConn; 665 pMixStream->uMagic = AUDMIXSTREAM_MAGIC; 637 666 638 667 RTCritSectLeave(&pSink->CritSect); … … 666 695 } 667 696 668 /** 669 * Enables or disables a mixer sink. 670 * 671 * @returns VBox status code.672 * Generally always VINF_SUCCESS unless the input is invalid.673 * Individual driver errors are suppressed and ignored.697 698 /** 699 * Starts playback/capturing on the mixer sink. 700 * 701 * @returns VBox status code. Generally always VINF_SUCCESS unless the input 702 * is invalid. Individual driver errors are suppressed and ignored. 674 703 * @param pSink Mixer sink to control. 675 * @param fEnable Whether to enable or disable the sink. 676 */ 677 int AudioMixerSinkEnable(PAUDMIXSINK pSink, bool fEnable) 704 */ 705 int AudioMixerSinkStart(PAUDMIXSINK pSink) 678 706 { 679 707 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 680 PDMAUDIOSTREAMCMD const enmCmd = fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE; 681 708 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 682 709 int rc = RTCritSectEnter(&pSink->CritSect); 683 710 AssertRCReturn(rc, rc); 711 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX]; 712 LogFunc(("Starting '%s'. Old status: %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus))); 684 713 685 714 /* 686 * Input sink and no recording source set? Return early without updating the stream status...715 * Make sure the sink and its streams are all stopped. 687 716 */ 688 if ( pSink->enmDir == PDMAUDIODIR_IN 689 && pSink->In.pStreamRecSource == NULL) 690 { 691 /** @todo r=bird: will the cause trouble for the stream status management? */ 692 } 717 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING)) 718 Assert(pSink->fStatus == AUDMIXSINK_STS_NONE); 693 719 else 694 720 { 721 LogFunc(("%s: This sink is still running!! Stop it before starting it again.\n", pSink->pszName)); 722 723 PAUDMIXSTREAM pStream; 724 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node) 725 { 726 /** @todo PDMAUDIOSTREAMCMD_STOP_NOW */ 727 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_DISABLE); 728 } 729 audioMixerSinkReset(pSink); 730 } 731 732 /* 733 * Send the command to the streams. 734 */ 735 if (pSink->enmDir == PDMAUDIODIR_OUT) 736 { 737 PAUDMIXSTREAM pStream; 738 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node) 739 { 740 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE); 741 } 742 } 743 else 744 { 745 AssertReturnStmt(pSink->enmDir == PDMAUDIODIR_IN, RTCritSectLeave(&pSink->CritSect), VERR_INTERNAL_ERROR_3); 746 if (pSink->In.pStreamRecSource) /* Any recording source set? */ 747 { 748 Assert(audioMixerSinkIsStreamInList(pSink, pSink->In.pStreamRecSource)); 749 audioMixerStreamCtlInternal(pSink->In.pStreamRecSource, PDMAUDIOSTREAMCMD_ENABLE); 750 } 751 } 752 753 /* 754 * Update the sink status. 755 */ 756 pSink->fStatus = AUDMIXSINK_STS_RUNNING; 757 758 LogRel2(("Audio Mixer: Started sink '%s': %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus))); 759 760 RTCritSectLeave(&pSink->CritSect); 761 return VINF_SUCCESS; 762 } 763 764 765 /** 766 * Helper for AudioMixerSinkDrainAndStop that calculates the max length a drain 767 * operation should take. 768 * 769 * @returns The drain deadline (relative to RTTimeNanoTS). 770 * @param pSink The sink. 771 * @param cbDmaLeftToDrain The number of bytes in the DMA buffer left to 772 * transfer into the mixbuf. 773 */ 774 static uint64_t audioMixerSinkDrainDeadline(PAUDMIXSINK pSink, uint32_t cbDmaLeftToDrain) 775 { 776 /* 777 * Calculate the max backend buffer size in mixbuf frames. 778 * (This is somewhat similar to audioMixerSinkUpdateOutputCalcFramesToRead.) 779 */ 780 uint32_t cFramesStreamMax = 0; 781 PAUDMIXSTREAM pMixStream; 782 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 783 { 784 /*LogFunc(("Stream '%s': %#x (%u frames)\n", pMixStream->pszName, pMixStream->fStatus, pMixStream->cFramesBackendBuffer));*/ 785 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE) 786 { 787 uint32_t cFrames = pMixStream->cFramesBackendBuffer; 788 if (PDMAudioPropsHz(&pMixStream->pStream->Props) == PDMAudioPropsHz(&pSink->MixBuf.Props)) 789 { /* likely */ } 790 else 791 cFrames = cFrames * PDMAudioPropsHz(&pSink->MixBuf.Props) / PDMAudioPropsHz(&pMixStream->pStream->Props); 792 if (cFrames > cFramesStreamMax) 793 { 794 Log4Func(("%s: cFramesStreamMax %u -> %u; %s\n", pSink->pszName, cFramesStreamMax, cFrames, pMixStream->pszName)); 795 cFramesStreamMax = cFrames; 796 } 797 } 798 } 799 800 /* 801 * Combine that with the pending DMA and mixbuf content, then convert 802 * to nanoseconds and apply a fudge factor to get a generous deadline. 803 */ 804 uint32_t const cFramesDmaAndMixBuf = PDMAudioPropsBytesToFrames(&pSink->MixBuf.Props, cbDmaLeftToDrain) 805 + AudioMixBufLive(&pSink->MixBuf); 806 uint64_t const cNsToDrainMax = PDMAudioPropsFramesToNano(&pSink->MixBuf.Props, cFramesDmaAndMixBuf + cFramesStreamMax); 807 uint64_t const nsDeadline = cNsToDrainMax * 2; 808 LogFlowFunc(("%s: cFramesStreamMax=%#x cFramesDmaAndMixBuf=%#x -> cNsToDrainMax=%RU64 -> %RU64\n", 809 pSink->pszName, cFramesStreamMax, cFramesDmaAndMixBuf, cNsToDrainMax, nsDeadline)); 810 return nsDeadline; 811 } 812 813 814 /** 815 * Kicks off the draining and stopping playback/capture on the mixer sink. 816 * 817 * For input streams this causes an immediate stop, as draining only makes sense 818 * to output stream in the VBox device context. 819 * 820 * @returns VBox status code. Generally always VINF_SUCCESS unless the input 821 * is invalid. Individual driver errors are suppressed and ignored. 822 * @param pSink Mixer sink to control. 823 * @param cbComming The number of bytes still left in the device's DMA 824 * buffers that the update job has yet to transfer. This 825 * is ignored for input streams. 826 */ 827 int AudioMixerSinkDrainAndStop(PAUDMIXSINK pSink, uint32_t cbComming) 828 { 829 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 830 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 831 832 int rc = RTCritSectEnter(&pSink->CritSect); 833 AssertRCReturn(rc, rc); 834 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX]; 835 LogFunc(("Draining '%s' with %#x bytes left. Old status: %s\n", 836 pSink->pszName, cbComming, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus) )); 837 838 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING) 839 { 695 840 /* 696 * Send the command to the streams. 841 * Output streams will be drained then stopped (all by the AIO thread). 842 * 843 * For streams we define that they shouldn't not be written to after we start draining, 844 * so we have to hold back sending the command to them till we've processed all the 845 * cbComming remaining bytes in the DMA buffer. 697 846 */ 698 if (pSink->enmDir == PDMAUDIODIR_IN) 699 { 700 /** @todo r=bird: we already check this. duh. */ 847 if (pSink->enmDir == PDMAUDIODIR_OUT) 848 { 849 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING)) 850 { 851 Assert(!(pSink->fStatus & (AUDMIXSINK_STS_DRAINED_DMA | AUDMIXSINK_STS_DRAINED_MIXBUF))); 852 853 /* Update the status and draining member. */ 854 pSink->cbDmaLeftToDrain = cbComming; 855 pSink->nsDrainDeadline = audioMixerSinkDrainDeadline(pSink, cbComming); 856 if (pSink->nsDrainDeadline > 0) 857 { 858 pSink->nsDrainStarted = RTTimeNanoTS(); 859 pSink->nsDrainDeadline += pSink->nsDrainStarted; 860 pSink->fStatus |= AUDMIXSINK_STS_DRAINING; 861 862 /* Kick the AIO thread so it can keep pushing data till we're out of this 863 status. (The device's DMA timer won't kick it any more, so we must.) */ 864 AudioMixerSinkSignalUpdateJob(pSink); 865 } 866 else 867 { 868 LogFunc(("%s: No active streams, doing an immediate stop.\n")); 869 PAUDMIXSTREAM pStream; 870 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node) 871 { 872 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_DISABLE); 873 } 874 audioMixerSinkReset(pSink); 875 } 876 } 877 else 878 AssertMsgFailed(("Already draining '%s': %s\n", 879 pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus))); 880 } 881 /* 882 * Input sinks are stopped immediately. 883 * 884 * It's the guest giving order here and we can't force it to accept data that's 885 * already in the buffer pipeline or anything. So, there can be no draining here. 886 */ 887 else 888 { 889 AssertReturnStmt(pSink->enmDir == PDMAUDIODIR_IN, RTCritSectLeave(&pSink->CritSect), VERR_INTERNAL_ERROR_3); 701 890 if (pSink->In.pStreamRecSource) /* Any recording source set? */ 702 891 { 703 PAUDMIXSTREAM pStream; 704 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node) 705 { 706 if (pStream == pSink->In.pStreamRecSource) 707 { 708 audioMixerStreamCtlInternal(pStream, enmCmd); 709 break; 710 } 711 } 892 Assert(audioMixerSinkIsStreamInList(pSink, pSink->In.pStreamRecSource)); 893 audioMixerStreamCtlInternal(pSink->In.pStreamRecSource, PDMAUDIOSTREAMCMD_DISABLE); 712 894 } 713 } 714 else if (pSink->enmDir == PDMAUDIODIR_OUT) 715 { 716 PAUDMIXSTREAM pStream; 717 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node) 718 { 719 audioMixerStreamCtlInternal(pStream, enmCmd); 720 } 721 } 722 else 723 AssertFailed(); 724 725 /* 726 * Update the sink status. 727 */ 728 if (fEnable) 729 { 730 /* Make sure to clear any other former flags again by assigning AUDMIXSINK_STS_RUNNING directly. */ 731 pSink->fStatus = AUDMIXSINK_STS_RUNNING; 732 } 733 else 734 { 735 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING) 736 { 737 /* Set the sink in a pending disable state first. 738 * The final status (disabled) will be set in the sink's iteration. */ 739 pSink->fStatus |= AUDMIXSINK_STS_PENDING_DISABLE; 740 741 /* Kick the AIO thread so it can keep pushing data till we're out of this status. */ 742 if (pSink->AIO.hEvent != NIL_RTSEMEVENT) 743 AudioMixerSinkSignalUpdateJob(pSink); 744 } 745 } 746 } 747 748 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX]; 749 LogRel2(("Audio Mixer: Set new status of sink '%s': %s (enmCmd=%s)\n", 750 pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus), PDMAudioStrmCmdGetName(enmCmd))); 751 895 audioMixerSinkReset(pSink); 896 } 897 } 898 else 899 LogFunc(("%s: Not running\n", pSink->pszName)); 900 901 LogRel2(("Audio Mixer: Started draining sink '%s': %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus))); 752 902 RTCritSectLeave(&pSink->CritSect); 753 903 return VINF_SUCCESS; 754 904 } 905 906 755 907 756 908 /** … … 773 925 if (RT_SUCCESS(rc)) 774 926 { 927 pSink->uMagic = AUDMIXSINK_MAGIC; 775 928 pSink->pParent = pMixer; 776 929 pSink->enmDir = enmDir; … … 823 976 PAUDIOMIXER pMixer = pSink->pParent; 824 977 AssertPtr(pMixer); 978 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC); 825 979 826 980 audioMixerRemoveSinkInternal(pMixer, pSink); … … 848 1002 LogFunc(("%s\n", pSink->pszName)); 849 1003 1004 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1005 pSink->uMagic = AUDMIXSINK_MAGIC_DEAD; 1006 850 1007 PAUDMIXSTREAM pStream, pStreamNext; 851 1008 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node) 852 1009 { 853 1010 audioMixerSinkRemoveStreamInternal(pSink, pStream); 854 audioMixerStreamDestroyInternal(pStream, pDevIns); 1011 audioMixerStreamDestroyInternal(pStream, pDevIns); /* (Unlike the other two, this frees the stream structure.) */ 855 1012 } 856 1013 … … 904 1061 { 905 1062 AssertPtrReturn(pSink, 0); 906 1063 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 907 1064 AssertMsg(pSink->enmDir == PDMAUDIODIR_IN, ("%s: Can't read from a non-input sink\n", pSink->pszName)); 908 1065 909 1066 int rc = RTCritSectEnter(&pSink->CritSect); 910 if (RT_FAILURE(rc)) 911 return 0; 1067 AssertRCReturn(rc, 0); 912 1068 913 1069 uint32_t cbReadable = 0; … … 947 1103 PAUDMIXSTREAM AudioMixerSinkGetRecordingSource(PAUDMIXSINK pSink) 948 1104 { 1105 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 949 1106 int rc = RTCritSectEnter(&pSink->CritSect); 950 if (RT_FAILURE(rc)) 951 return NULL; 1107 AssertRCReturn(rc, NULL); 952 1108 953 1109 AssertMsg(pSink->enmDir == PDMAUDIODIR_IN, ("Specified sink is not an input sink\n")); … … 971 1127 { 972 1128 AssertPtrReturn(pSink, 0); 973 1129 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 974 1130 AssertMsg(pSink->enmDir == PDMAUDIODIR_OUT, ("%s: Can't write to a non-output sink\n", pSink->pszName)); 975 1131 976 1132 int rc = RTCritSectEnter(&pSink->CritSect); 977 if (RT_FAILURE(rc)) 978 return 0; 1133 AssertRCReturn(rc, 0); 979 1134 980 1135 uint32_t cbWritable = 0; 981 1136 982 1137 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING) 983 && !(pSink->fStatus & AUDMIXSINK_STS_ PENDING_DISABLE))1138 && !(pSink->fStatus & AUDMIXSINK_STS_DRAINING)) 984 1139 { 985 1140 cbWritable = AudioMixBufFreeBytes(&pSink->MixBuf); … … 1004 1159 { 1005 1160 AssertPtrReturn(pSink, PDMAUDIODIR_INVALID); 1161 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1006 1162 1007 1163 /** @todo the sink direction should be static... */ … … 1027 1183 if (!pSink) 1028 1184 return AUDMIXSINK_STS_NONE; 1185 AssertPtr(pSink); 1186 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1029 1187 1030 1188 int rc2 = RTCritSectEnter(&pSink->CritSect); 1031 if (RT_FAILURE(rc2)) 1032 return AUDMIXSINK_STS_NONE; 1189 AssertRCReturn(rc2, AUDMIXSINK_STS_NONE); 1033 1190 1034 1191 /* If the dirty flag is set, there is unprocessed data in the sink. */ … … 1053 1210 if (!pSink) 1054 1211 return false; 1212 AssertPtr(pSink); 1213 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1055 1214 1056 1215 int rc2 = RTCritSectEnter(&pSink->CritSect); 1057 if (RT_FAILURE(rc2)) 1058 return false; 1216 AssertRCReturn(rc2, false); 1059 1217 1060 1218 const bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING; … … 1081 1239 { 1082 1240 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 1241 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1083 1242 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 1084 1243 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); … … 1216 1375 void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream) 1217 1376 { 1377 AssertPtr(pSink); 1378 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1379 if (pStream) 1380 Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC); 1381 1218 1382 int rc2 = RTCritSectEnter(&pSink->CritSect); 1219 1383 AssertRC(rc2); … … 1248 1412 1249 1413 /** 1250 * Re sets the sink's state.1251 * 1252 * @param pSink Sink to reset.1253 */ 1254 static void audioMixerSinkReset(PAUDMIXSINK pSink)1414 * Removes all attached streams from a given sink. 1415 * 1416 * @param pSink Sink to remove attached streams from. 1417 */ 1418 void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink) 1255 1419 { 1256 1420 if (!pSink) 1257 1421 return; 1258 1422 1423 int rc2 = RTCritSectEnter(&pSink->CritSect); 1424 AssertRC(rc2); 1425 1426 audioMixerSinkRemoveAllStreamsInternal(pSink); 1427 1428 pSink->cStreams = 0; 1429 1430 rc2 = RTCritSectLeave(&pSink->CritSect); 1431 AssertRC(rc2); 1432 } 1433 1434 /** 1435 * Resets the sink's state. 1436 * 1437 * @param pSink Sink to reset. 1438 */ 1439 static void audioMixerSinkReset(PAUDMIXSINK pSink) 1440 { 1441 if (!pSink) 1442 return; 1443 1259 1444 LogFunc(("[%s]\n", pSink->pszName)); 1260 1445 … … 1266 1451 /* Reset status. */ 1267 1452 pSink->fStatus = AUDMIXSINK_STS_NONE; 1268 }1269 1270 /**1271 * Removes all attached streams from a given sink.1272 *1273 * @param pSink Sink to remove attached streams from.1274 */1275 void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)1276 {1277 if (!pSink)1278 return;1279 1280 int rc2 = RTCritSectEnter(&pSink->CritSect);1281 AssertRC(rc2);1282 1283 audioMixerSinkRemoveAllStreamsInternal(pSink);1284 1285 pSink->cStreams = 0;1286 1287 rc2 = RTCritSectLeave(&pSink->CritSect);1288 AssertRC(rc2);1289 1453 } 1290 1454 … … 1320 1484 { 1321 1485 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 1486 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1322 1487 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER); 1323 1488 AssertReturn(AudioHlpPcmPropsAreValid(pPCMProps), VERR_INVALID_PARAMETER); … … 1467 1632 { 1468 1633 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 1634 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1635 if (pStream) 1636 Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC); 1469 1637 1470 1638 int rc = RTCritSectEnter(&pSink->CritSect); 1471 if (RT_FAILURE(rc)) 1472 return rc; 1639 AssertRCReturn(rc, rc); 1473 1640 1474 1641 rc = audioMixerSinkSetRecSourceInternal(pSink, pStream); … … 1490 1657 { 1491 1658 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 1659 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1492 1660 AssertPtrReturn(pVol, VERR_INVALID_POINTER); 1493 1661 1494 1662 int rc = RTCritSectEnter(&pSink->CritSect); 1495 if (RT_FAILURE(rc)) 1496 return rc; 1663 AssertRCReturn(rc, rc); 1497 1664 1498 1665 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME)); … … 1579 1746 pSink->tsLastUpdatedMs = RTTimeMilliTS(); 1580 1747 1581 /* 1582 * Deal with pending disable. The general case is that we reset 1583 * the sink when all streams have been disabled, however input is 1584 * currently a special case where we only care about the one 1585 * recording source... 1586 */ 1587 if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE) 1588 { 1589 #if 1 1590 uint32_t const cStreams = 1; 1591 uint32_t cStreamsDisabled = 1; 1592 pMixStream = pSink->In.pStreamRecSource; 1593 #else 1594 uint32_t const cStreams = pSink->cStreams; 1595 uint32_t cStreamsDisabled = pSink->cStreams; 1596 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 1597 #endif 1598 { 1599 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED) 1600 { 1601 PDMAUDIOSTREAMSTATE const enmState = pMixStream->pConn->pfnStreamGetState(pMixStream->pConn, pMixStream->pStream); 1602 if (enmState >= PDMAUDIOSTREAMSTATE_ENABLED) 1603 cStreamsDisabled--; 1604 } 1605 } 1606 Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams)); 1607 if (cStreamsDisabled == cStreams) 1608 audioMixerSinkReset(pSink); 1609 } 1610 1748 Assert(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING)); 1611 1749 return rc; 1612 1750 } … … 1674 1812 { 1675 1813 PAUDMIXSTREAM pMixStream; 1814 Assert(!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_MIXBUF) || AudioMixBufUsed(&pSink->MixBuf) == 0); 1676 1815 1677 1816 /* … … 1814 1953 * Update the dirty flag for what it's worth. 1815 1954 */ 1816 if (AudioMixBufUsed(&pSink->MixBuf) )1955 if (AudioMixBufUsed(&pSink->MixBuf) > 0) 1817 1956 pSink->fStatus |= AUDMIXSINK_STS_DIRTY; 1818 1957 else … … 1838 1977 1839 1978 /* Update last updated timestamp. */ 1840 pSink->tsLastUpdatedMs = RTTimeMilliTS(); 1979 uint64_t const nsNow = RTTimeNanoTS(); 1980 pSink->tsLastUpdatedMs = nsNow / RT_NS_1MS; 1841 1981 1842 1982 /* … … 1844 1984 * We reset the sink when all streams have been disabled. 1845 1985 */ 1846 if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE) 1847 { 1986 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING)) 1987 { /* likely, till we get to the end */ } 1988 else if (nsNow <= pSink->nsDrainDeadline) 1989 { 1990 /* Have we drained the mixbuf now? If so, update status and send drain 1991 command to streams. (As mentioned elsewhere we don't want to confuse 1992 driver code by sending drain command while there is still data to write.) */ 1993 Assert((pSink->fStatus & AUDMIXSINK_STS_DIRTY) == (AudioMixBufUsed(&pSink->MixBuf) > 0 ? AUDMIXSINK_STS_DIRTY : 0)); 1994 if ((pSink->fStatus & (AUDMIXSINK_STS_DRAINED_MIXBUF | AUDMIXSINK_STS_DIRTY)) == 0) 1995 { 1996 LogFunc(("Sink '%s': Setting AUDMIXSINK_STS_DRAINED_MIXBUF and sending drain command to streams (after %RU64 ns).\n", 1997 pSink->pszName, nsNow - pSink->nsDrainStarted)); 1998 pSink->fStatus |= AUDMIXSINK_STS_DRAINED_MIXBUF; 1999 2000 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 2001 { 2002 pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, PDMAUDIOSTREAMCMD_DRAIN); 2003 } 2004 } 2005 2006 /* Check if all streams has stopped, and if so we stop the sink. */ 1848 2007 uint32_t const cStreams = pSink->cStreams; 1849 2008 uint32_t cStreamsDisabled = pSink->cStreams; … … 1857 2016 } 1858 2017 } 1859 Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams)); 1860 if (cStreamsDisabled == cStreams) 1861 audioMixerSinkReset(pSink); 2018 2019 if (cStreamsDisabled != cStreams) 2020 Log3Func(("Sink '%s': %u out of %u streams disabled (after %RU64 ns).\n", 2021 pSink->pszName, cStreamsDisabled, cStreams, nsNow - pSink->nsDrainStarted)); 2022 else 2023 { 2024 LogFunc(("Sink '%s': All %u streams disabled. Drain done after %RU64 ns.\n", 2025 pSink->pszName, cStreamsDisabled, cStreams, nsNow - pSink->nsDrainStarted)); 2026 audioMixerSinkReset(pSink); /* clears the status */ 2027 } 2028 } 2029 else 2030 { 2031 /* Draining timed out. Just do an instant stop. */ 2032 LogFunc(("Sink '%s': pending disable timed out after %RU64 ns!\n", pSink->pszName, nsNow - pSink->nsDrainStarted)); 2033 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) 2034 { 2035 pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, PDMAUDIOSTREAMCMD_DISABLE); 2036 } 2037 audioMixerSinkReset(pSink); /* clears the status */ 1862 2038 } 1863 2039 … … 1874 2050 { 1875 2051 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 2052 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1876 2053 int rc = RTCritSectEnter(&pSink->CritSect); 1877 2054 AssertRCReturn(rc, rc); … … 1908 2085 PAUDMIXSINK pSink = (PAUDMIXSINK)pvUser; 1909 2086 AssertPtr(pSink); 2087 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1910 2088 RT_NOREF(hThreadSelf); 1911 2089 … … 1919 2097 1920 2098 RTCritSectEnter(&pSink->CritSect); 1921 if (pSink->fStatus & (AUDMIXSINK_STS_RUNNING | AUDMIXSINK_STS_ PENDING_DISABLE))2099 if (pSink->fStatus & (AUDMIXSINK_STS_RUNNING | AUDMIXSINK_STS_DRAINING)) 1922 2100 { 1923 2101 /* … … 1945 2123 * DMA timer as it has normally stopped running at this point. 1946 2124 */ 1947 if (!(pSink->fStatus & AUDMIXSINK_STS_ PENDING_DISABLE))2125 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING)) 1948 2126 { /* likely */ } 1949 2127 else … … 1989 2167 { 1990 2168 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 2169 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 1991 2170 int rc = RTCritSectEnter(&pSink->CritSect); 1992 2171 AssertRCReturn(rc, rc); … … 2068 2247 { 2069 2248 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 2249 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 2070 2250 int rc = RTCritSectEnter(&pSink->CritSect); 2071 2251 AssertRCReturn(rc, rc); … … 2119 2299 */ 2120 2300 AssertReturn(pSink, offStream); 2301 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 2121 2302 AssertReturn(pCircBuf, offStream); 2122 2303 Assert(RTCritSectIsOwner(&pSink->CritSect)); 2304 RT_NOREF(idStream); 2123 2305 2124 2306 /* … … 2129 2311 uint32_t cbToTransfer = RT_MIN(cbCircBufReadable, cbSinkWritable); 2130 2312 /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */ 2131 cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer);2132 2133 Log3Func(("idStream=% #x: cbSinkWritable=%#RX32 cbCircBufReadable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",2313 uint32_t const cbToTransfer2 = cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer); 2314 2315 Log3Func(("idStream=%u: cbSinkWritable=%#RX32 cbCircBufReadable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n", 2134 2316 idStream, cbSinkWritable, cbCircBufReadable, cbToTransfer, offStream)); 2135 RT_NOREF(idStream); 2317 AssertMsg(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING) || cbCircBufReadable == pSink->cbDmaLeftToDrain, 2318 ("cbCircBufReadable=%#x cbDmaLeftToDrain=%#x\n", cbCircBufReadable, pSink->cbDmaLeftToDrain)); 2136 2319 2137 2320 /* … … 2149 2332 Assert(cbWritten <= cbSrcBuf); 2150 2333 2151 Log2Func(("idStream=% #x: %#RX32/%#zx bytes read @%#RX64\n", idStream, cbWritten, cbSrcBuf, offStream));2334 Log2Func(("idStream=%u: %#RX32/%#zx bytes read @%#RX64\n", idStream, cbWritten, cbSrcBuf, offStream)); 2152 2335 #ifdef VBOX_WITH_DTRACE 2153 2336 VBOXDD_AUDIO_MIXER_SINK_AIO_OUT(idStream, cbWritten, offStream); … … 2166 2349 cbToTransfer -= cbWritten; 2167 2350 } 2351 2352 /* 2353 * Advance drain status. 2354 */ 2355 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING)) 2356 { /* likely for most of the playback time ... */ } 2357 else if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_DMA)) 2358 { 2359 if (cbToTransfer2 >= pSink->cbDmaLeftToDrain) 2360 { 2361 Assert(cbToTransfer2 == pSink->cbDmaLeftToDrain); 2362 Log3Func(("idStream=%u/'%s': Setting AUDMIXSINK_STS_DRAINED_DMA.\n", idStream, pSink->pszName)); 2363 pSink->cbDmaLeftToDrain = 0; 2364 pSink->fStatus |= AUDMIXSINK_STS_DRAINED_DMA; 2365 } 2366 else 2367 { 2368 pSink->cbDmaLeftToDrain -= cbToTransfer2; 2369 Log3Func(("idStream=%u/'%s': still %#x bytes left in the DMA buffer\n", 2370 idStream, pSink->pszName, pSink->cbDmaLeftToDrain)); 2371 } 2372 } 2373 else 2374 Assert(cbToTransfer2 == 0); 2168 2375 2169 2376 return offStream; … … 2191 2398 */ 2192 2399 AssertReturn(pSink, offStream); 2400 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 2193 2401 AssertReturn(pCircBuf, offStream); 2194 2402 Assert(RTCritSectIsOwner(&pSink->CritSect)); … … 2204 2412 cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer); 2205 2413 2206 Log3Func(("idStream=% #x: cbSinkReadable=%#RX32 cbCircBufWritable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",2414 Log3Func(("idStream=%u: cbSinkReadable=%#RX32 cbCircBufWritable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n", 2207 2415 idStream, cbSinkReadable, cbCircBufWritable, cbToTransfer, offStream)); 2208 2416 RT_NOREF(idStream); … … 2270 2478 { 2271 2479 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 2480 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 2272 2481 return RTSemEventSignal(pSink->AIO.hEvent); 2273 2482 } … … 2283 2492 { 2284 2493 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 2494 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 2285 2495 return RTCritSectEnter(&pSink->CritSect); 2286 2496 } … … 2296 2506 { 2297 2507 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 2508 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 2298 2509 return RTCritSectTryEnter(&pSink->CritSect); 2299 2510 } … … 2320 2531 * @param pVolMaster Master volume to set. 2321 2532 */ 2322 static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)2533 static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, PCPDMAUDIOVOLUME pVolMaster) 2323 2534 { 2324 2535 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 2536 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 2325 2537 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER); 2326 2538 … … 2373 2585 { 2374 2586 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 2587 Assert(pSink->uMagic == AUDMIXSINK_MAGIC); 2375 2588 RT_NOREF(enmOp); 2376 2589 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); … … 2431 2644 static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd) 2432 2645 { 2646 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC); 2433 2647 AssertPtrReturn(pMixStream->pConn, VERR_AUDIO_STREAM_NOT_READY); 2434 2648 AssertPtrReturn(pMixStream->pStream, VERR_AUDIO_STREAM_NOT_READY); … … 2452 2666 static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream) 2453 2667 { 2668 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC); 2669 2454 2670 /* 2455 2671 * Reset the mixer status to start with. … … 2521 2737 2522 2738 /** 2523 * Destroys a mixer stream, internal version.2739 * Destroys & frees a mixer stream, internal version. 2524 2740 * 2525 2741 * Worker for audioMixerSinkDestroyInternal and AudioMixerStreamDestroy. … … 2533 2749 2534 2750 LogFunc(("%s\n", pMixStream->pszName)); 2751 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC); 2535 2752 2536 2753 if (pMixStream->pConn) /* Stream has a connector interface present? */ … … 2575 2792 return; 2576 2793 2794 /** @todo r=bird: Wrng critsect for audioMixerSinkRemoveStreamInternal */ 2577 2795 int rc2 = RTCritSectEnter(&pMixStream->CritSect); 2578 2796 AssertRC(rc2); -
trunk/src/VBox/Devices/Audio/AudioMixer.h
r88957 r88991 42 42 typedef struct AUDIOMIXER 43 43 { 44 /** Magic value (AUDIOMIXER_MAGIC). */ 45 uintptr_t uMagic; 44 46 /** The mixer's name. */ 45 47 char *pszName; 46 /** The mixer's critical section. */47 RTCRITSECT CritSect;48 48 /** The master volume of this mixer. */ 49 49 PDMAUDIOVOLUME VolMaster; … … 54 54 /** Mixer flags. See AUDMIXER_FLAGS_XXX. */ 55 55 uint32_t fFlags; 56 /** The mixer's critical section. */ 57 RTCRITSECT CritSect; 56 58 } AUDIOMIXER; 57 59 /** Pointer to an audio mixer instance. */ 58 60 typedef AUDIOMIXER *PAUDIOMIXER; 61 62 /** Value for AUDIOMIXER::uMagic. (Attilio Joseph "Teo" Macero) */ 63 #define AUDIOMIXER_MAGIC UINT32_C(0x19251030) 64 /** Value for AUDIOMIXER::uMagic after destruction. */ 65 #define AUDIOMIXER_MAGIC_DEAD UINT32_C(0x20080219) 59 66 60 67 /** @name AUDMIXER_FLAGS_XXX - For AudioMixerCreate(). … … 77 84 /** List entry on AUDMIXSINK::lstStreams. */ 78 85 RTLISTNODE Node; 79 /** Name of this stream. */ 80 char *pszName; 81 /** The statistics prefix. */ 82 char *pszStatPrefix; 83 /** Sink this stream is attached to. */ 84 PAUDMIXSINK pSink; 86 /** Magic value (AUDMIXSTREAM_MAGIC). */ 87 uint32_t uMagic; 88 /** The backend buffer size in frames (for draining deadline calc). */ 89 uint32_t cFramesBackendBuffer; 85 90 /** Stream status of type AUDMIXSTREAM_STATUS_. */ 86 91 uint32_t fStatus; … … 91 96 * from the mixer buffer and to the drivers. */ 92 97 bool fUnreliable; 98 /** Name of this stream. */ 99 char *pszName; 100 /** The statistics prefix. */ 101 char *pszStatPrefix; 102 /** Sink this stream is attached to. */ 103 PAUDMIXSINK pSink; 93 104 /** Pointer to audio connector being used. */ 94 105 PPDMIAUDIOCONNECTOR pConn; … … 104 115 /** Pointer to an audio mixer stream. */ 105 116 typedef AUDMIXSTREAM *PAUDMIXSTREAM; 117 118 /** Value for AUDMIXSTREAM::uMagic. (Jan Erik Kongshaug) */ 119 #define AUDMIXSTREAM_MAGIC UINT32_C(0x19440704) 120 /** Value for AUDMIXSTREAM::uMagic after destruction. */ 121 #define AUDMIXSTREAM_MAGIC_DEAD UINT32_C(0x20191105) 122 106 123 107 124 /** @name AUDMIXSTREAM_STATUS_XXX - mixer stream status. … … 133 150 /** List entry on AUDIOMIXER::lstSinks. */ 134 151 RTLISTNODE Node; 152 /** Magic value (AUDMIXSINK_MAGIC). */ 153 uint32_t uMagic; 154 /** The sink direction (either PDMAUDIODIR_IN or PDMAUDIODIR_OUT). */ 155 PDMAUDIODIR enmDir; 135 156 /** Pointer to mixer object this sink is bound to. */ 136 157 PAUDIOMIXER pParent; 137 158 /** Name of this sink. */ 138 159 char *pszName; 139 /** The sink direction (either PDMAUDIODIR_IN or PDMAUDIODIR_OUT). */140 PDMAUDIODIR enmDir;141 160 /** The sink's PCM format (i.e. the guest device side). */ 142 161 PDMAUDIOPCMPROPS PCMProps; 143 162 /** Sink status bits - AUDMIXSINK_STS_XXX. */ 144 163 uint32_t fStatus; 164 /** Number of bytes to be transferred from the device DMA buffer before the 165 * streams will be put into draining mode. */ 166 uint32_t cbDmaLeftToDrain; 167 /** The deadline for draining if it's pending. */ 168 uint64_t nsDrainDeadline; 169 /** When the draining startet (for logging). */ 170 uint64_t nsDrainStarted; 145 171 /** Number of streams assigned. */ 146 172 uint8_t cStreams; … … 215 241 } AUDMIXSINK; 216 242 243 /** Value for AUDMIXSINK::uMagic. (Sir George Martin) */ 244 #define AUDMIXSINK_MAGIC UINT32_C(0x19260103) 245 /** Value for AUDMIXSINK::uMagic after destruction. */ 246 #define AUDMIXSINK_MAGIC_DEAD UINT32_C(0x20160308) 247 248 217 249 /** @name AUDMIXSINK_STS_XXX - Sink status bits. 218 250 * @{ */ … … 221 253 /** The sink is active and running. */ 222 254 #define AUDMIXSINK_STS_RUNNING RT_BIT(0) 223 /** The sink is in a pending disable state. */ 224 #define AUDMIXSINK_STS_PENDING_DISABLE RT_BIT(1) 255 /** Draining the buffers and pending stop - output only. */ 256 #define AUDMIXSINK_STS_DRAINING RT_BIT(1) 257 /** Drained the DMA buffer. */ 258 #define AUDMIXSINK_STS_DRAINED_DMA RT_BIT(2) 259 /** Drained the mixer buffer, only waiting for streams (drivers) now. */ 260 #define AUDMIXSINK_STS_DRAINED_MIXBUF RT_BIT(3) 225 261 /** Dirty flag. 226 262 * - For output sinks this means that there is data in the sink which has not … … 228 264 * - For input sinks this means that there is data in the sink which has been 229 265 * recorded but not transferred to the destination yet. */ 230 #define AUDMIXSINK_STS_DIRTY RT_BIT( 2)266 #define AUDMIXSINK_STS_DIRTY RT_BIT(4) 231 267 /** @} */ 232 268 … … 258 294 int AudioMixerSinkCreateStream(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOSTREAMCFG pCfg, 259 295 PPDMDEVINS pDevIns, PAUDMIXSTREAM *ppStream); 260 int AudioMixerSinkEnable(PAUDMIXSINK pSink, bool fEnable); 296 int AudioMixerSinkStart(PAUDMIXSINK pSink); 297 int AudioMixerSinkDrainAndStop(PAUDMIXSINK pSink, uint32_t cbComming); 261 298 void AudioMixerSinkDestroy(PAUDMIXSINK pSink, PPDMDEVINS pDevIns); 262 299 uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink); -
trunk/src/VBox/Devices/Audio/DevHdaStream.cpp
r88954 r88991 905 905 if (pSink) 906 906 { 907 if (fEnable && pStreamR3->State.pAioRegSink != pSink)907 if (fEnable) 908 908 { 909 if (pStreamR3->State.pAioRegSink )909 if (pStreamR3->State.pAioRegSink != pSink) 910 910 { 911 rc = AudioMixerSinkRemoveUpdateJob(pStreamR3->State.pAioRegSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3); 912 AssertRC(rc); 911 if (pStreamR3->State.pAioRegSink) 912 { 913 rc = AudioMixerSinkRemoveUpdateJob(pStreamR3->State.pAioRegSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3); 914 AssertRC(rc); 915 } 916 rc = AudioMixerSinkAddUpdateJob(pSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3, 917 pStreamShared->State.Cfg.Device.cMsSchedulingHint); 918 AssertLogRelRC(rc); 919 pStreamR3->State.pAioRegSink = RT_SUCCESS(rc) ? pSink : NULL; 913 920 } 914 rc = AudioMixerSinkAddUpdateJob(pSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3, 915 pStreamShared->State.Cfg.Device.cMsSchedulingHint); 916 AssertLogRelRC(rc); 917 pStreamR3->State.pAioRegSink = RT_SUCCESS(rc) ? pSink : NULL; 921 rc = AudioMixerSinkStart(pSink); 918 922 } 919 if (RT_SUCCESS(rc)) 920 rc = AudioMixerSinkEnable(pSink, fEnable); 923 else 924 rc = AudioMixerSinkDrainAndStop(pSink, 925 pStreamR3->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf) : 0); 921 926 } 922 927 if ( RT_SUCCESS(rc) -
trunk/src/VBox/Devices/Audio/DevIchAc97.cpp
r88954 r88991 974 974 } 975 975 } 976 977 if (RT_SUCCESS(rc)) 978 rc = AudioMixerSinkStart(pSink); 976 979 } 977 980 else 981 { 978 982 rc = ichac97R3StreamClose(pStream); 979 980 if (RT_SUCCESS(rc)) 981 { 982 /* First, enable or disable the stream and the stream's sink, if any. */ 983 rc = AudioMixerSinkEnable(pSink, fEnable); 983 if (RT_SUCCESS(rc)) 984 rc = AudioMixerSinkDrainAndStop(pSink, 985 pStreamCC->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStreamCC->State.pCircBuf) : 0); 984 986 } 985 987 -
trunk/src/VBox/Devices/Audio/DevSB16.cpp
r88955 r88991 2045 2045 } 2046 2046 2047 /* First, enable or disable the stream and the stream's sink. */ 2048 rc = AudioMixerSinkEnable(pSink, fEnable); 2049 AssertRCReturn(rc, rc); 2047 /* Tell the mixer. */ 2048 if (fEnable) 2049 { 2050 rc = AudioMixerSinkStart(pSink); 2051 AssertRCReturn(rc, rc); 2052 } 2053 else 2054 { 2055 rc = AudioMixerSinkDrainAndStop(pSink, pStream->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStream->State.pCircBuf) : 0); 2056 AssertRCReturn(rc, rc); 2057 } 2050 2058 2051 2059 pStream->State.fEnabled = fEnable; -
trunk/src/VBox/Devices/Audio/DrvAudio.cpp
r88898 r88991 251 251 /** The current pre-buffering read offset. */ 252 252 uint32_t offPreBuf; 253 /** Number of bytes we've pre buffered. */253 /** Number of bytes we've pre-buffered. */ 254 254 uint32_t cbPreBuffered; 255 255 /** The pre-buffering threshold expressed in bytes. */ … … 363 363 RTREQPOOL hReqPool; 364 364 365 /** Handle to the disable-iteration timer. */366 TMTIMERHANDLE hTimer;367 /** Set if hTimer is armed. */368 bool volatile fTimerArmed;369 /** Unique name for the the disable-iteration timer. */370 char szTimerName[23];371 372 365 #ifdef VBOX_WITH_AUDIO_ENUM 373 366 /** Handle to the timer for delayed re-enumeration of backend devices. */ … … 547 540 { 548 541 PDMHOSTAUDIOSTREAMSTATE enmState = pThis->pHostDrvAudio->pfnStreamGetState(pThis->pHostDrvAudio, pStreamEx->pBackend); 549 Assert(enmState > PDMHOSTAUDIOSTREAMSTATE_INVALID && enmState < PDMHOSTAUDIOSTREAMSTATE_END);550 542 Log9Func(("%s: %s\n", pStreamEx->Core.szName, PDMHostAudioStreamStateGetName(enmState) )); 543 Assert( enmState > PDMHOSTAUDIOSTREAMSTATE_INVALID 544 && enmState < PDMHOSTAUDIOSTREAMSTATE_END 545 && (enmState != PDMHOSTAUDIOSTREAMSTATE_DRAINING || pStreamEx->Guest.Cfg.enmDir == PDMAUDIODIR_OUT)); 551 546 return enmState; 552 547 } 553 548 Log9Func(("%s: not-working\n", pStreamEx->Core.szName)); 554 549 return PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING; 550 } 551 552 553 /** 554 * Worker for drvAudioStreamProcessBackendStateChange that completes draining. 555 */ 556 DECLINLINE(void) drvAudioStreamProcessBackendStateChangeWasDraining(PDRVAUDIOSTREAM pStreamEx) 557 { 558 Log(("drvAudioStreamProcessBackendStateChange: Stream '%s': Done draining - disabling stream.\n", pStreamEx->Core.szName)); 559 pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PENDING_DISABLE); 560 drvAudioStreamResetInternal(pStreamEx); 555 561 } 556 562 … … 583 589 584 590 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING: 585 /* The stream has stopped working. Switch to noplay mode. */ 591 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE: 592 /* The stream has stopped working or is inactive. Switch stop any draining & to noplay mode. */ 593 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE) 594 drvAudioStreamProcessBackendStateChangeWasDraining(pStreamEx); 586 595 if (enmDir == PDMAUDIODIR_OUT) 587 596 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY; … … 600 609 or not, resetting the stream state. */ 601 610 drvAudioStreamResetInternal(pStreamEx); 611 break; 612 613 case PDMHOSTAUDIOSTREAMSTATE_DRAINING: 614 /* Complete the draining. May race the iterate code. */ 615 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE) 616 drvAudioStreamProcessBackendStateChangeWasDraining(pStreamEx); 602 617 break; 603 618 … … 611 626 break; 612 627 613 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE: 614 /* Stream is now inactive. Switch to noplay mode. */ 615 if (enmDir == PDMAUDIODIR_OUT) 616 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY; 628 case PDMHOSTAUDIOSTREAMSTATE_DRAINING: 629 /* We do all we need to do when issuing the DRAIN command. */ 630 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE); 617 631 break; 618 632 … … 2420 2434 { 2421 2435 if ( (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY /* don't really need this check, do we? */) 2422 && enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY) 2436 && ( enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY 2437 || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING) ) 2423 2438 { 2424 2439 /** @todo Backend will change to explicit methods here, so please don't simplify … … 2509 2524 2510 2525 /** 2511 * @callback_method_impl{FNTMTIMERDRV}2512 */2513 static DECLCALLBACK(void) drvAudioEmergencyIterateTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)2514 {2515 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);2516 RT_NOREF(hTimer, pvUser);2517 RTCritSectEnter(&pThis->CritSect);2518 2519 /*2520 * Iterate any stream with the pending-disable flag set.2521 */2522 uint32_t cMilliesToNext = 0;2523 PDRVAUDIOSTREAM pStreamEx, pStreamExNext;2524 RTListForEachSafe(&pThis->lstStreams, pStreamEx, pStreamExNext, DRVAUDIOSTREAM, ListEntry)2525 {2526 if ( pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC2527 && pStreamEx->cRefs >= 1)2528 {2529 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)2530 {2531 drvAudioStreamIterateInternal(pThis, pStreamEx);2532 2533 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)2534 cMilliesToNext = 10;2535 }2536 }2537 }2538 2539 /*2540 * Re-arm the timer if we still got streams in the pending state.2541 */2542 if (cMilliesToNext)2543 {2544 pThis->fTimerArmed = true;2545 PDMDrvHlpTimerSetMillies(pDrvIns, pThis->hTimer, cMilliesToNext);2546 }2547 else2548 pThis->fTimerArmed = false;2549 2550 RTCritSectLeave(&pThis->CritSect);2551 }2552 2553 2554 /**2555 2526 * Controls an audio stream. 2556 2527 * … … 2578 2549 if (!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED)) 2579 2550 { 2580 /* Is a pending disable outstanding? Then disablefirst. */2551 /* Are we still draining this stream? Then we must disable it first. */ 2581 2552 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE) 2553 { 2554 LogFunc(("Stream '%s' is still draining - disabling...\n", pStreamEx->Core.szName)); 2582 2555 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE); 2556 AssertRC(rc); 2557 if (drvAudioStreamGetBackendState(pThis, pStreamEx) != PDMHOSTAUDIOSTREAMSTATE_DRAINING) 2558 { 2559 pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PENDING_DISABLE); 2560 drvAudioStreamResetInternal(pStreamEx); 2561 rc = VINF_SUCCESS; 2562 } 2563 } 2564 2583 2565 if (RT_SUCCESS(rc)) 2584 2566 { … … 2597 2579 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF; 2598 2580 break; 2581 case PDMHOSTAUDIOSTREAMSTATE_DRAINING: 2582 AssertFailed(); 2599 2583 case PDMHOSTAUDIOSTREAMSTATE_OKAY: 2600 2584 pStreamEx->Out.enmPlayState = pStreamEx->Out.cbPreBufThreshold > 0 … … 2629 2613 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED) 2630 2614 { 2631 /* 2632 * For playback (output) streams first mark the host stream as pending disable, 2633 * so that the rest of the remaining audio data will be played first before 2634 * closing the stream. 2635 */ 2636 if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT) 2637 { 2638 LogFunc(("[%s] Pending disable/pause\n", pStreamEx->Core.szName)); 2639 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE; 2640 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus); 2641 2642 /* Schedule a follow up timer to the pending-disable state. We cannot rely 2643 on the device to provide further callouts to finish the state transition. 2644 10ms is taking out of thin air and may be too course grained, we should 2645 really consider the amount of unplayed buffer in the backend and what not... */ 2646 if (!pThis->fTimerArmed) 2647 { 2648 LogFlowFunc(("Arming emergency pending-disable hack...\n")); 2649 int rc2 = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hTimer, 10 /*ms*/); 2650 AssertRC(rc2); 2651 pThis->fTimerArmed = true; 2652 } 2653 } 2654 2655 /* Can we close the host stream as well (not in pending disable mode)? */ 2656 if (!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)) 2657 { 2658 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE); 2659 if (RT_SUCCESS(rc)) 2660 drvAudioStreamResetOnDisable(pStreamEx); 2661 } 2615 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE); 2616 LogFunc(("DISABLE '%s': Backend DISABLE -> %Rrc\n", pStreamEx->Core.szName, rc)); 2617 if (RT_SUCCESS(rc)) /** @todo ignore this and reset it anyway? */ 2618 drvAudioStreamResetOnDisable(pStreamEx); 2662 2619 } 2663 2620 break; … … 2688 2645 break; 2689 2646 2647 case PDMAUDIOSTREAMCMD_DRAIN: 2648 /* 2649 * Only for output streams and we don't want this command more than once. 2650 */ 2651 AssertReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT, VERR_INVALID_FUNCTION); 2652 AssertBreak(!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)); 2653 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED) 2654 { 2655 rc = VERR_INTERNAL_ERROR_2; 2656 switch (pStreamEx->Out.enmPlayState) 2657 { 2658 case DRVAUDIOPLAYSTATE_PREBUF: 2659 if (pStreamEx->Out.cbPreBuffered > 0) 2660 { 2661 LogFunc(("DRAIN '%s': Initiating draining of pre-buffered data...\n", 2662 pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState))); 2663 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_COMMITTING; 2664 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE; 2665 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus); 2666 break; 2667 } 2668 RT_FALL_THROUGH(); 2669 case DRVAUDIOPLAYSTATE_NOPLAY: 2670 case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING: 2671 case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE: 2672 LogFunc(("DRAIN '%s': Nothing to drain (enmPlayState=%s)\n", 2673 pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState))); 2674 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE); 2675 AssertRC(rc); 2676 drvAudioStreamResetOnDisable(pStreamEx); 2677 break; 2678 2679 case DRVAUDIOPLAYSTATE_PLAY: 2680 case DRVAUDIOPLAYSTATE_PLAY_PREBUF: 2681 LogFunc(("DRAIN '%s': Initiating backend draining (enmPlayState=%s -> NOPLAY) ...\n", 2682 pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState))); 2683 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY; 2684 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DRAIN); 2685 if (RT_SUCCESS(rc)) 2686 { 2687 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE; 2688 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus); 2689 } 2690 else 2691 { 2692 LogFunc(("DRAIN '%s': Backend DRAIN failed with %Rrc, disabling the stream instead...\n", 2693 pStreamEx->Core.szName)); 2694 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE); 2695 AssertRC(rc); 2696 drvAudioStreamResetOnDisable(pStreamEx); 2697 } 2698 break; 2699 2700 case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING: 2701 LogFunc(("DRAIN '%s': Initiating draining of pre-buffered data (already committing)...\n", 2702 pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState))); 2703 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE; 2704 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus); 2705 break; 2706 2707 /* no default */ 2708 case DRVAUDIOPLAYSTATE_INVALID: 2709 case DRVAUDIOPLAYSTATE_END: 2710 AssertFailedBreak(); 2711 } 2712 } 2713 break; 2714 2690 2715 default: 2691 2716 rc = VERR_NOT_IMPLEMENTED; … … 2992 3017 2993 3018 /* 2994 * Pending disable is really what we're here for. This only happens to output streams. 2995 */ 2996 int rc = VINF_SUCCESS; 3019 * Pending disable is really what we're here for. 3020 * 3021 * This only happens to output streams. We ASSUME the caller (MixerBuffer) 3022 * implements a timeout on the draining, so we skip that here. 3023 */ 2997 3024 if (!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)) 2998 3025 { /* likely until we get to the end of the stream at least. */ } … … 3000 3027 { 3001 3028 AssertReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT, VINF_SUCCESS); 3002 /** @todo Add a timeout to these proceedings. A few times that of the reported3003 * buffer size or something like that. */3004 3029 3005 3030 /* 3006 * Check if we have any data we need to write to the backend, try 3007 * move it now. 3031 * Move pre-buffered samples to the backend. 3008 3032 */ 3009 /** @todo r=bird: It is possible the device has data buffered (e.g. 3010 * internal DMA buffer (HDA) or mixing buffer (HDA + AC'97). We're 3011 * not taking that into account here. I also suspect that neither is 3012 * the device/mixer code, and that if the guest is too quick disabling 3013 * the stream, it will just remain there till the next time something 3014 * is played. That means that this code and associated timer hack 3015 * should probably not be here at all. */ 3016 uint32_t cFramesLive = 0; 3017 switch (pStreamEx->Out.enmPlayState) 3018 { 3019 case DRVAUDIOPLAYSTATE_PLAY: /* Nothing prebuffered. */ 3020 case DRVAUDIOPLAYSTATE_PLAY_PREBUF: /* Not pre-buffering for current device. */ 3021 case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE: /* Output device isn't ready, drop it. */ /** @todo check state? */ 3022 case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING: /* No output device yet. */ 3023 case DRVAUDIOPLAYSTATE_NOPLAY: 3024 case DRVAUDIOPLAYSTATE_INVALID: 3025 case DRVAUDIOPLAYSTATE_END: 3026 /* no default, want warnings. */ 3027 break; 3028 case DRVAUDIOPLAYSTATE_PREBUF: 3029 case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING: 3030 if (pStreamEx->Out.cbPreBuffered > 0) 3033 if (pStreamEx->Out.enmPlayState == DRVAUDIOPLAYSTATE_PREBUF_COMMITTING) 3034 { 3035 if (pStreamEx->Out.cbPreBuffered > 0) 3036 { 3037 uint32_t cbIgnored = 0; 3038 drvAudioStreamPreBufComitting(pThis, pStreamEx, NULL, 0, &cbIgnored); 3039 Log3Func(("Stream '%s': Transferred %#x bytes\n", pStreamEx->Core.szName, cbIgnored)); 3040 } 3041 if (pStreamEx->Out.cbPreBuffered == 0) 3042 { 3043 Log3Func(("Stream '%s': No more pre-buffered data -> NOPLAY + backend DRAIN\n", pStreamEx->Core.szName)); 3044 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY; 3045 3046 int rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DRAIN); 3047 if (RT_FAILURE(rc)) 3031 3048 { 3032 /* Must check the backend state here first and only try commit the 3033 pre-buffered samples if the backend is in working order. */ 3034 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx); 3035 /* ^^^ (checks pThis->pHostDrvAudio != NULL too) */ 3036 if ( (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY) 3037 && enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY) 3038 { 3039 uint32_t cbIgnored = 0; 3040 drvAudioStreamPreBufComitting(pThis, pStreamEx, NULL, 0, &cbIgnored); 3041 cFramesLive = PDMAudioPropsBytesToFrames(&pStreamEx->Core.Props, pStreamEx->Out.cbPreBuffered); 3042 } 3043 else 3044 Log3Func(("[%s] Skipping committing pre-buffered samples (status: %#x backend: %s)!\n", 3045 pStreamEx->Core.szName, pStreamEx->fStatus, PDMHostAudioStreamStateGetName(enmBackendState))); 3046 } 3047 break; 3048 } 3049 Log3Func(("[%s] cFramesLive=%RU32\n", pStreamEx->Core.szName, cFramesLive)); 3050 if (cFramesLive == 0) 3051 { 3052 /* 3053 * Tell the backend to start draining the stream, that is, 3054 * play the remaining buffered data and stop. 3055 */ 3056 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DRAIN); 3057 if (rc == VERR_NOT_SUPPORTED) /* Not all backends support draining yet. */ 3058 rc = VINF_SUCCESS; 3059 /** @todo r=bird: We could probably just skip this next check, as if drainig 3060 * failes, we should definitely try disable the stream. Maybe the 3061 * host audio device was unplugged and we're leaving this stream in a 3062 * bogus state. */ 3063 if (RT_SUCCESS(rc)) 3064 { 3065 /* 3066 * Before we disable the stream, check if the backend has finished playing the buffered data. 3067 */ 3068 uint32_t cbPending; 3069 if (!pThis->pHostDrvAudio || !pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional. */ 3070 cbPending = 0; 3071 else 3072 { 3073 cbPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pStreamEx->pBackend); 3074 Log3Func(("[%s] cbPending=%RU32 (%#RX32)\n", pStreamEx->Core.szName, cbPending, cbPending)); 3075 } 3076 if (cbPending == 0) 3077 { 3078 /* 3079 * Okay, disable it. 3080 */ 3081 LogFunc(("[%s] Disabling pending stream\n", pStreamEx->Core.szName)); 3049 LogFunc(("Stream '%s': Backend DRAIN failed with %Rrc, disabling the stream instead...\n", 3050 pStreamEx->Core.szName)); 3082 3051 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE); 3083 if (RT_SUCCESS(rc)) 3084 { 3085 pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PENDING_DISABLE); 3086 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus); 3087 drvAudioStreamResetInternal(pStreamEx); 3088 } 3089 /** @todo r=bird: This log entry sounds a rather fishy to be honest... Any 3090 * backend which would do that, or genuinely need this? */ 3091 else 3092 LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pStreamEx->Core.szName, rc)); 3052 AssertRC(rc); 3053 drvAudioStreamResetOnDisable(pStreamEx); 3093 3054 } 3094 3055 } 3095 3056 } 3057 else 3058 Assert(pStreamEx->Out.enmPlayState == DRVAUDIOPLAYSTATE_NOPLAY); 3059 3060 /* 3061 * Check the backend status to see if it's still draining and to 3062 * update our status when it stops doing so. 3063 */ 3064 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx); 3065 if (enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING) 3066 { 3067 uint32_t cbIgnored = 0; 3068 pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, NULL, 0, &cbIgnored); 3069 } 3070 else 3071 { 3072 LogFunc(("Stream '%s': Backend finished draining.\n", pStreamEx->Core.szName)); 3073 drvAudioStreamResetOnDisable(pStreamEx); 3074 } 3096 3075 } 3097 3076 … … 3099 3078 pStreamEx->nsLastIterated = RTTimeNanoTS(); 3100 3079 3101 if (RT_FAILURE(rc)) 3102 LogFunc(("[%s] Failed with %Rrc\n", pStreamEx->Core.szName, rc)); 3103 3104 return rc; 3080 return VINF_SUCCESS; /** @todo r=bird: What can the caller do with an error status here? */ 3105 3081 } 3106 3082 … … 3234 3210 */ 3235 3211 uint32_t cbWritable = 0; 3236 DRVAUDIOPLAYSTATE const enmPlayMode = pStreamEx->Out.enmPlayState; 3212 DRVAUDIOPLAYSTATE const enmPlayMode = pStreamEx->Out.enmPlayState; 3213 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx); 3237 3214 if ( PDMAudioStrmStatusCanWrite(pStreamEx->fStatus) 3238 && pThis->pHostDrvAudio != NULL) 3215 && pThis->pHostDrvAudio != NULL 3216 && enmBackendState != PDMHOSTAUDIOSTREAMSTATE_DRAINING) 3239 3217 { 3240 3218 switch (enmPlayMode) … … 3246 3224 case DRVAUDIOPLAYSTATE_PLAY_PREBUF: 3247 3225 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY); 3248 Assert( drvAudioStreamGetBackendState(pThis, pStreamEx)== PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);3226 Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */); 3249 3227 cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend); 3250 3228 break; … … 3284 3262 to move the data. */ 3285 3263 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY); 3286 Assert( drvAudioStreamGetBackendState(pThis, pStreamEx)== PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);3264 Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */); 3287 3265 uint32_t const cbMin = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Props, 8); 3288 3266 cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend); … … 3308 3286 3309 3287 RTCritSectLeave(&pThis->CritSect); 3310 Log3Func(("[%s] cbWritable=%RU32 (%RU64ms) enmPlayMode=%s\n", pStreamEx->Core.szName, cbWritable, 3311 PDMAudioPropsBytesToMilli(&pStreamEx->Host.Cfg.Props, cbWritable), drvAudioPlayStateName(enmPlayMode) )); 3288 Log3Func(("[%s] cbWritable=%RU32 (%RU64ms) enmPlayMode=%s enmBackendState=%s\n", 3289 pStreamEx->Core.szName, cbWritable, PDMAudioPropsBytesToMilli(&pStreamEx->Host.Cfg.Props, cbWritable), 3290 drvAudioPlayStateName(enmPlayMode), PDMHostAudioStreamStateGetName(enmBackendState) )); 3312 3291 return cbWritable; 3313 3292 } … … 3349 3328 && (enmDir == PDMAUDIODIR_IN ? pThis->In.fEnabled : pThis->Out.fEnabled) 3350 3329 && ( enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY 3330 || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING 3351 3331 || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_INITIALIZING )) 3352 3332 enmState = enmDir == PDMAUDIODIR_IN ? PDMAUDIOSTREAMSTATE_ENABLED_READABLE : PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE; … … 4858 4838 STAMUNIT_BYTES, "Total bytes read."); 4859 4839 #endif 4860 4861 /*4862 * Create a timer to do finish closing output streams in PENDING_DISABLE state.4863 *4864 * The device won't call us again after it has disabled a the stream and this is4865 * a real problem for truely cyclic buffer backends like DSound which will just4866 * continue to loop and loop if not stopped.4867 */4868 RTStrPrintf(pThis->szTimerName, sizeof(pThis->szTimerName), "AudioIterate-%u", pDrvIns->iInstance);4869 rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_VIRTUAL, drvAudioEmergencyIterateTimer, NULL /*pvUser*/,4870 0 /*fFlags*/, pThis->szTimerName, &pThis->hTimer);4871 AssertRCReturn(rc, rc);4872 4840 4873 4841 #ifdef VBOX_WITH_AUDIO_ENUM -
trunk/src/VBox/Devices/Audio/DrvHostAudioAlsa.cpp
r88966 r88991 56 56 #include <alsa/asoundlib.h> 57 57 #include <alsa/control.h> /* For device enumeration. */ 58 #include <alsa/version.h> 58 59 #include "DrvHostAudioAlsaStubs.h" 59 60 … … 1125 1126 { 1126 1127 RT_NOREF(pInterface); 1127 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID); 1128 1129 return PDMHOSTAUDIOSTREAMSTATE_OKAY; 1128 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 1129 AssertPtrReturn(pStreamALSA, PDMHOSTAUDIOSTREAMSTATE_INVALID); 1130 1131 PDMHOSTAUDIOSTREAMSTATE enmStreamState = PDMHOSTAUDIOSTREAMSTATE_OKAY; 1132 snd_pcm_state_t enmAlsaState = snd_pcm_state(pStreamALSA->hPCM); 1133 if (enmAlsaState == SND_PCM_STATE_DRAINING) 1134 enmStreamState = PDMHOSTAUDIOSTREAMSTATE_DRAINING; 1135 #if (((SND_LIB_MAJOR) << 16) | ((SND_LIB_MAJOR) << 8) | (SND_LIB_SUBMINOR)) >= 0x10002 /* was added in 1.0.2 */ 1136 else if (enmAlsaState == SND_PCM_STATE_DISCONNECTED) 1137 enmStreamState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING; 1138 #endif 1139 1140 Log5Func(("Stream '%s': ALSA state=%s -> %s\n", 1141 pStreamALSA->Cfg.szName, snd_pcm_state_name(enmAlsaState), PDMHostAudioStreamStateGetName(enmStreamState) )); 1142 return enmStreamState; 1130 1143 } 1131 1144 … … 1274 1287 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1275 1288 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 1276 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);1277 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);1278 1289 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); 1279 1290 Log4Func(("@%#RX64: pvBuf=%p cbBuf=%#x (%u) state=%s - %s\n", pStreamALSA->offInternal, pvBuf, cbBuf, cbBuf, 1280 1291 snd_pcm_state_name(snd_pcm_state(pStreamALSA->hPCM)), pStreamALSA->Cfg.szName)); 1292 if (cbBuf) 1293 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 1294 else 1295 { 1296 /* Fend off draining calls. */ 1297 *pcbWritten = 0; 1298 return VINF_SUCCESS; 1299 } 1281 1300 1282 1301 /* -
trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp
r88959 r88991 224 224 /** The Direct Sound capturing interface. */ 225 225 LPDIRECTSOUNDCAPTURE8 pDSC; 226 /** A drain stop timer that makes sure a draining stream will be227 * properly stopped (seem the non-loop mode is a bit buggy). */228 TMTIMERHANDLE hDrainTimer;229 226 /** List of streams (DSOUNDSTREAM). 230 227 * Requires CritSect ownership. */ … … 1963 1960 if (pStreamDS->Out.pDSB) 1964 1961 { 1965 /* Don't stop draining buffers. They'll stop by themselves. */ 1966 if (pStreamDS->Out.fDrain) 1967 LogFunc(("Stream '%s' is draining\n", pStreamDS->Cfg.szName)); 1968 else 1969 { 1970 rc = drvHostDSoundStreamStopPlayback(pThis, pStreamDS, true /*fReset*/); 1971 if (RT_SUCCESS(rc)) 1972 LogRel3(("DSound: Stopped playback on '%s'.\n", pStreamDS->Cfg.szName)); 1973 } 1962 rc = drvHostDSoundStreamStopPlayback(pThis, pStreamDS, true /*fReset*/); 1963 if (RT_SUCCESS(rc)) 1964 LogRel3(("DSound: Stopped playback on '%s'.\n", pStreamDS->Cfg.szName)); 1974 1965 } 1975 1966 } … … 2097 2088 2098 2089 /** 2099 * This is used by the timer function as well as when arming the timer.2100 *2101 * @param pThis The DSound host audio driver instance data.2102 * @param msNow A current RTTimeMilliTS() value.2103 */2104 static void drvHostDSoundDrainTimerWorker(PDRVHOSTDSOUND pThis, uint64_t msNow)2105 {2106 /*2107 * Go thru the stream list and look at draining streams.2108 */2109 uint64_t msNext = UINT64_MAX;2110 RTCritSectEnter(&pThis->CritSect);2111 PDSOUNDSTREAM pCur;2112 RTListForEach(&pThis->HeadStreams, pCur, DSOUNDSTREAM, ListEntry)2113 {2114 if ( pCur->Cfg.enmDir == PDMAUDIODIR_OUT2115 && pCur->Out.fDrain)2116 {2117 uint64_t const msCurDeadline = pCur->Out.msDrainDeadline;2118 if (msCurDeadline > 0 && msCurDeadline < msNext)2119 {2120 if (msCurDeadline > msNow)2121 msNext = pCur->Out.msDrainDeadline;2122 else2123 {2124 LogRel2(("DSound: Stopping draining of '%s' {%s} ...\n",2125 pCur->Cfg.szName, drvHostDSoundStreamStatusString(pCur)));2126 if (pCur->Out.pDSB)2127 {2128 HRESULT hrc = IDirectSoundBuffer8_Stop(pCur->Out.pDSB);2129 if (FAILED(hrc))2130 LogRelMax(64, ("DSound: Failed to stop draining stream '%s': %Rhrc\n", pCur->Cfg.szName, hrc));2131 }2132 pCur->Out.fDrain = false;2133 }2134 }2135 }2136 }2137 2138 /*2139 * Re-arm the timer if necessary.2140 */2141 if (msNext != UINT64_MAX)2142 PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hDrainTimer, msNext - msNow);2143 RTCritSectLeave(&pThis->CritSect);2144 }2145 2146 2147 /**2148 * @callback_method_impl{FNTMTIMERDRV,2149 * This is a hack to ensure that draining streams stop playing.}2150 */2151 static DECLCALLBACK(void) drvHostDSoundDrainStopHackTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)2152 {2153 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);2154 RT_NOREF(hTimer, pvUser);2155 drvHostDSoundDrainTimerWorker(pThis, RTTimeMilliTS());2156 }2157 2158 2159 /**2160 2090 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain} 2161 2091 */ … … 2185 2115 pStreamDS->Out.msDrainDeadline = PDMAudioPropsBytesToMilli(&pStreamDS->Cfg.Props, pStreamDS->cbBufSize) + msNow; 2186 2116 pStreamDS->Out.fDrain = true; 2187 drvHostDSoundDrainTimerWorker(pThis, msNow);2188 2117 } 2189 2118 else … … 2416 2345 AssertPtrReturn(pStreamDS, PDMHOSTAUDIOSTREAMSTATE_INVALID); 2417 2346 2418 LogFlowFunc(("returns OKAY for '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS))); 2419 return PDMHOSTAUDIOSTREAMSTATE_OKAY; 2347 if ( pStreamDS->Cfg.enmDir != PDMAUDIODIR_OUT 2348 || !pStreamDS->Out.fDrain) 2349 { 2350 LogFlowFunc(("returns OKAY for '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS))); 2351 return PDMHOSTAUDIOSTREAMSTATE_OKAY; 2352 } 2353 LogFlowFunc(("returns DRAINING for '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS))); 2354 return PDMHOSTAUDIOSTREAMSTATE_DRAINING; 2420 2355 } 2421 2356 … … 2430 2365 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream; 2431 2366 AssertPtrReturn(pStreamDS, 0); 2432 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);2433 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);2367 if (cbBuf) 2368 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 2434 2369 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); 2435 2370 … … 2529 2464 drvHostDSoundStreamStatusString(pStreamDS) )); 2530 2465 } 2466 else if ( pStreamDS->Out.fDrain 2467 && RTTimeMilliTS() >= pStreamDS->Out.msDrainDeadline) 2468 { 2469 LogRel2(("DSound: Stopping draining of '%s' {%s} ...\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS))); 2470 if (pStreamDS->Out.pDSB) 2471 { 2472 HRESULT hrc = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB); 2473 if (FAILED(hrc)) 2474 LogRelMax(64, ("DSound: Failed to stop draining stream '%s': %Rhrc\n", pStreamDS->Cfg.szName, hrc)); 2475 } 2476 pStreamDS->Out.fDrain = false; 2477 pStreamDS->fEnabled = false; 2478 } 2479 2531 2480 return VINF_SUCCESS; 2532 2481 } … … 2756 2705 RTListInit(&pThis->HeadStreams); 2757 2706 pThis->pDrvIns = pDrvIns; 2758 pThis->hDrainTimer = NIL_TMTIMERHANDLE;2759 2707 /* IBase */ 2760 2708 pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface; … … 2842 2790 LogRel2(("DSound: Notification client is disabled (ver %#RX64)\n", RTSystemGetNtVersion())); 2843 2791 #endif 2844 2845 /*2846 * Create a timer that helps making sure draining streams actually stop playing.2847 * (Seem dsound doesn't stop by itself in many cases.)2848 */2849 int rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_REAL, drvHostDSoundDrainStopHackTimer, NULL /*pvUser*/, 0 /*fFlags*/,2850 "DSound drain", &pThis->hDrainTimer);2851 AssertRCReturn(rc, rc);2852 2792 2853 2793 /* -
trunk/src/VBox/Devices/Audio/DrvHostAudioOss.cpp
r88958 r88991 461 461 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream; 462 462 463 /** @todo this might be a little optimisitic... */ 463 464 pStreamOSS->fDraining = false; 464 465 … … 494 495 if (pStreamOSS->Cfg.enmDir == PDMAUDIODIR_IN) 495 496 rc = VINF_SUCCESS; /** @todo apparently nothing to do here? */ 496 else if (pStreamOSS->fDraining)497 {498 LogFlow(("Ignoring stream disable because we're draining...\n"));499 rc = VINF_SUCCESS; /** @todo apparently nothing to do here? */500 }501 497 else 502 498 { 499 /* 500 * If we're still draining, try kick the thread before we try disable the stream. 501 */ 502 if (pStreamOSS->fDraining) 503 { 504 LogFuncFlow(("Trying to cancel draining...\n")); 505 if (pStreamOSS->hThreadDrain != NIL_RTTHREAD) 506 { 507 RTThreadPoke(pStreamOSS->hThreadDrain); 508 rc = RTThreadWait(pStreamOSS->hThreadDrain, 1 /*ms*/, NULL); 509 if (RT_SUCCESS(rc) || rc == VERR_INVALID_HANDLE) 510 pStreamOSS->fDraining = false; 511 else 512 LogFuncFlow(("Failed to cancel draining (%Rrc)\n", rc)); 513 } 514 else 515 { 516 LogFuncFlow(("Thread handle is NIL, so we can't be draining\n")); 517 pStreamOSS->fDraining = false; 518 } 519 } 520 503 521 /** @todo Official documentation says this isn't the right way to stop playback. 504 522 * It may work in some implementations but fail in all others... Suggest … … 710 728 { 711 729 RT_NOREF(pInterface); 712 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID); 713 return PDMHOSTAUDIOSTREAMSTATE_OKAY; 730 POSSAUDIOSTREAM pStreamOSS = (POSSAUDIOSTREAM)pStream; 731 AssertPtrReturn(pStreamOSS, PDMHOSTAUDIOSTREAMSTATE_INVALID); 732 if (!pStreamOSS->fDraining) 733 return PDMHOSTAUDIOSTREAMSTATE_OKAY; 734 return PDMHOSTAUDIOSTREAMSTATE_DRAINING; 714 735 } 715 736 -
trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp
r88958 r88991 1279 1279 if (enmOpState == PA_OPERATION_RUNNING) 1280 1280 { 1281 /** @todo consider corking it immediately instead, as that's what the caller 1282 * wants now... */ 1281 1283 LogFlowFunc(("Drain (%p) already running on '%s', skipping.\n", pStreamPA->pDrainOp, pStreamPA->Cfg.szName)); 1282 1284 pa_threaded_mainloop_unlock(pThis->pMainLoop); … … 1558 1560 PDRVHOSTPULSEAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPULSEAUDIO, IHostAudio); 1559 1561 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID); 1562 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1563 AssertPtrReturn(pStreamPA, PDMHOSTAUDIOSTREAMSTATE_INVALID); 1560 1564 1561 1565 /* Check PulseAudio's general status. */ 1566 PDMHOSTAUDIOSTREAMSTATE enmBackendStreamState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING; 1562 1567 if (pThis->pContext) 1563 1568 { 1564 pa_context_state_t const enmState = pa_context_get_state(pThis->pContext); 1565 if (PA_CONTEXT_IS_GOOD(enmState)) 1566 { 1567 /** @todo should we check the actual stream state? */ 1568 return PDMHOSTAUDIOSTREAMSTATE_OKAY; 1569 } 1570 LogFunc(("non-good context state: %d\n", enmState)); 1569 pa_context_state_t const enmCtxState = pa_context_get_state(pThis->pContext); 1570 if (PA_CONTEXT_IS_GOOD(enmCtxState)) 1571 { 1572 pa_stream_state_t const enmStreamState = pa_stream_get_state(pStreamPA->pStream); 1573 if (PA_STREAM_IS_GOOD(enmState)) 1574 { 1575 if (enmState != PA_STREAM_CREATING) 1576 { 1577 if ( pStreamPA->Cfg.enmDir != PDMAUDIODIR_OUT 1578 || pStreamPA->pDrainOp == NULL 1579 || pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_RUNNING) 1580 enmBackendStreamState = PDMHOSTAUDIOSTREAMSTATE_OKAY; 1581 else 1582 enmBackendStreamState = PDMHOSTAUDIOSTREAMSTATE_DRAINING; 1583 } 1584 else 1585 enmBackendStreamState = PDMHOSTAUDIOSTREAMSTATE_INITIALIZING; 1586 } 1587 else 1588 LogFunc(("non-good PA stream state: %d\n", enmStreamState)); 1589 } 1590 else 1591 LogFunc(("non-good PA context state: %d\n", enmCtxState)); 1571 1592 } 1572 1593 else 1573 1594 LogFunc(("No context!\n")); 1574 return PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING; 1595 LogFlowFunc(("returns %s for stream '%s'\n", PDMHostAudioStreamStateGetName(enmBackendStreamState), pStreamPA->Cfg.szName)); 1596 return enmBackendStreamState; 1575 1597 } 1576 1598 … … 1585 1607 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1586 1608 AssertPtrReturn(pStreamPA, VERR_INVALID_POINTER); 1587 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);1588 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);1589 1609 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); 1610 if (cbBuf) 1611 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 1612 else 1613 { 1614 /* Fend off draining calls. */ 1615 *pcbWritten = 0; 1616 return VINF_SUCCESS; 1617 } 1590 1618 1591 1619 pa_threaded_mainloop_lock(pThis->pMainLoop); -
trunk/src/VBox/Devices/Audio/DrvHostAudioWasApi.cpp
r88959 r88991 252 252 IMMDevice *pIDeviceOutput; 253 253 254 /** A drain stop timer that makes sure a draining stream will be properly255 * stopped (mainly for clean state and to reduce resource usage). */256 TMTIMERHANDLE hDrainTimer;257 254 /** List of streams (DRVHOSTAUDIOWASSTREAM). 258 255 * Requires CritSect ownership. */ … … 2140 2137 2141 2138 int rc = VINF_SUCCESS; 2142 if (!pStreamWas->fDraining) 2143 { 2144 if (pStreamWas->fStarted) 2145 { 2146 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop(); 2147 LogFlowFunc(("Stop(%s) returns %Rhrc\n", pStreamWas->Cfg.szName, hrc)); 2148 if (FAILED(hrc)) 2149 { 2150 LogRelMax(64, ("WasAPI: Stopping '%s' failed (disable): %Rhrc\n", pStreamWas->Cfg.szName, hrc)); 2151 rc = VERR_GENERAL_FAILURE; 2152 } 2153 pStreamWas->fStarted = false; 2154 } 2155 } 2156 else 2157 { 2158 LogFunc(("Stream '%s' is still draining...\n", pStreamWas->Cfg.szName)); 2159 Assert(pStreamWas->fStarted); 2139 if (pStreamWas->fStarted) 2140 { 2141 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop(); 2142 LogFlowFunc(("Stop(%s) returns %Rhrc\n", pStreamWas->Cfg.szName, hrc)); 2143 if (FAILED(hrc)) 2144 { 2145 LogRelMax(64, ("WasAPI: Stopping '%s' failed (disable): %Rhrc\n", pStreamWas->Cfg.szName, hrc)); 2146 rc = VERR_GENERAL_FAILURE; 2147 } 2148 pStreamWas->fStarted = false; 2149 pStreamWas->fDraining = false; 2160 2150 } 2161 2151 … … 2241 2231 2242 2232 /** 2243 * This is used by the timer function as well as when arming the timer.2244 *2245 * @param pThis The DSound host audio driver instance data.2246 * @param msNow A current RTTimeMilliTS() value.2247 */2248 static void drvHostWasDrainTimerWorker(PDRVHOSTAUDIOWAS pThis, uint64_t msNow)2249 {2250 /*2251 * Go thru the stream list and look at draining streams.2252 */2253 uint64_t msNext = UINT64_MAX;2254 RTCritSectRwEnterShared(&pThis->CritSectStreamList);2255 PDRVHOSTAUDIOWASSTREAM pCur;2256 RTListForEach(&pThis->StreamHead, pCur, DRVHOSTAUDIOWASSTREAM, ListEntry)2257 {2258 if ( pCur->fDraining2259 && pCur->Cfg.enmDir == PDMAUDIODIR_OUT)2260 {2261 Assert(pCur->fStarted);2262 uint64_t msCurDeadline = pCur->msDrainDeadline;2263 if (msCurDeadline > 0 && msCurDeadline < msNext)2264 {2265 /* Take the lock and recheck: */2266 RTCritSectEnter(&pCur->CritSect);2267 msCurDeadline = pCur->msDrainDeadline;2268 if ( pCur->fDraining2269 && msCurDeadline > 02270 && msCurDeadline < msNext)2271 {2272 if (msCurDeadline > msNow)2273 msNext = pCur->msDrainDeadline;2274 else2275 {2276 LogRel2(("WasAPI: Stopping draining of '%s' {%s} ...\n",2277 pCur->Cfg.szName, drvHostWasStreamStatusString(pCur)));2278 HRESULT hrc = pCur->pDevCfg->pIAudioClient->Stop();2279 if (FAILED(hrc))2280 LogRelMax(64, ("WasAPI: Failed to stop draining stream '%s': %Rhrc\n", pCur->Cfg.szName, hrc));2281 pCur->fDraining = false;2282 pCur->fStarted = false;2283 }2284 }2285 RTCritSectLeave(&pCur->CritSect);2286 }2287 }2288 }2289 2290 /*2291 * Re-arm the timer if necessary.2292 */2293 if (msNext != UINT64_MAX)2294 PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hDrainTimer, msNext - msNow);2295 RTCritSectRwLeaveShared(&pThis->CritSectStreamList);2296 }2297 2298 2299 /**2300 * @callback_method_impl{FNTMTIMERDRV,2301 * This is to ensure that draining streams stop properly.}2302 */2303 static DECLCALLBACK(void) drvHostWasDrainStopTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)2304 {2305 PDRVHOSTAUDIOWAS pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTAUDIOWAS);2306 RT_NOREF(hTimer, pvUser);2307 drvHostWasDrainTimerWorker(pThis, RTTimeMilliTS());2308 }2309 2310 2311 /**2312 2233 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain} 2313 2234 */ 2314 2235 static DECLCALLBACK(int) drvHostAudioWasHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 2315 2236 { 2316 PDRVHOSTAUDIOWAS pThis = RT_FROM_MEMBER(pInterface, DRVHOSTAUDIOWAS, IHostAudio);2237 RT_NOREF(pInterface); 2317 2238 PDRVHOSTAUDIOWASSTREAM pStreamWas = (PDRVHOSTAUDIOWASSTREAM)pStream; 2318 2239 AssertReturn(pStreamWas->Cfg.enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER); … … 2323 2244 /* 2324 2245 * If the stram was started, calculate when the buffered data has finished 2325 * playing and switch to drain mode. Use the drain timer callback worker 2326 * to re-arm the timer or to stop the playback. 2246 * playing and switch to drain mode. DrvAudio will keep on calling 2247 * pfnStreamPlay with an empty buffer while we're draining, so we'll use 2248 * that for checking the deadline and finally stopping the stream. 2327 2249 */ 2328 2250 RTCritSectEnter(&pStreamWas->CritSect); … … 2363 2285 RTCritSectLeave(&pStreamWas->CritSect); 2364 2286 2365 /*2366 * Always do drain timer processing to re-arm the timer or actually stop2367 * this stream (and others). (Must be done _after_ unlocking the stream.)2368 */2369 drvHostWasDrainTimerWorker(pThis, RTTimeMilliTS());2370 2371 2287 LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostWasStreamStatusString(pStreamWas))); 2372 2288 return rc; … … 2537 2453 { 2538 2454 if (RT_SUCCESS(pStreamWas->pDevCfg->rcSetup)) 2539 enmState = PDMHOSTAUDIOSTREAMSTATE_OKAY; 2455 { 2456 if (!pStreamWas->fDraining) 2457 enmState = PDMHOSTAUDIOSTREAMSTATE_OKAY; 2458 else 2459 { 2460 Assert(pStreamWas->Cfg.enmDir == PDMAUDIODIR_OUT); 2461 enmState = PDMHOSTAUDIOSTREAMSTATE_DRAINING; 2462 } 2463 } 2540 2464 else if ( pStreamWas->pDevCfg->rcSetup == VERR_AUDIO_STREAM_INIT_IN_PROGRESS 2541 2465 || pStreamWas->fSwitchingDevice ) … … 2563 2487 PDRVHOSTAUDIOWASSTREAM pStreamWas = (PDRVHOSTAUDIOWASSTREAM)pStream; 2564 2488 AssertPtrReturn(pStreamWas, VERR_INVALID_POINTER); 2565 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);2566 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);2567 2489 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); 2490 if (cbBuf) 2491 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 2568 2492 Assert(PDMAudioPropsIsSizeAligned(&pStreamWas->Cfg.Props, cbBuf)); 2569 2493 … … 2615 2539 uint32_t const cFramesToWrite = PDMAudioPropsBytesToFrames(&pStreamWas->Cfg.Props, cbToWrite); 2616 2540 Assert(PDMAudioPropsFramesToBytes(&pStreamWas->Cfg.Props, cFramesToWrite) == cbToWrite); 2617 Log3Func(("@% RX64: cFramesPending=%#x -> cbWritable=%#x cbToWrite=%#x cFramesToWrite=%#x {%s}\n",2541 Log3Func(("@%#RX64: cFramesPending=%#x -> cbWritable=%#x cbToWrite=%#x cFramesToWrite=%#x {%s}\n", 2618 2542 pStreamWas->offInternal, cFramesPending, cbWritable, cbToWrite, cFramesToWrite, 2619 2543 drvHostWasStreamStatusString(pStreamWas) )); … … 2673 2597 2674 2598 /* 2599 * Do draining deadline processing. 2600 */ 2601 uint64_t const msNow = RTTimeMilliTS(); 2602 if ( !pStreamWas->fDraining 2603 || msNow < pStreamWas->msDrainDeadline) 2604 { /* likely */ } 2605 else 2606 { 2607 LogRel2(("WasAPI: Stopping draining of '%s' {%s} ...\n", pStreamWas->Cfg.szName, drvHostWasStreamStatusString(pStreamWas))); 2608 HRESULT hrc = pStreamWas->pDevCfg->pIAudioClient->Stop(); 2609 if (FAILED(hrc)) 2610 LogRelMax(64, ("WasAPI: Failed to stop draining stream '%s': %Rhrc\n", pStreamWas->Cfg.szName, hrc)); 2611 pStreamWas->fDraining = false; 2612 pStreamWas->fStarted = false; 2613 } 2614 2615 /* 2675 2616 * Done. 2676 2617 */ 2677 2618 uint64_t const msPrev = pStreamWas->msLastTransfer; 2678 uint64_t const msNow = RTTimeMilliTS();2679 2619 if (cbWritten) 2680 2620 pStreamWas->msLastTransfer = msNow; … … 2996 2936 */ 2997 2937 pThis->pDrvIns = pDrvIns; 2998 pThis->hDrainTimer = NIL_TMTIMERHANDLE;2999 2938 pThis->hEvtCachePurge = NIL_RTSEMEVENTMULTI; 3000 2939 #if 0 … … 3139 3078 3140 3079 pThis->pNotifyClient->lockLeave(); 3141 3142 /*3143 * We need a timer for draining streams.3144 */3145 rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_REAL, drvHostWasDrainStopTimer, NULL /*pvUser*/, 0 /*fFlags*/,3146 "WasAPI drain", &pThis->hDrainTimer);3147 AssertRCReturn(rc, rc);3148 3080 3149 3081 #if 0
Note:
See TracChangeset
for help on using the changeset viewer.