Changeset 88450 in vbox
- Timestamp:
- Apr 9, 2021 8:00:48 PM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioAlsa.cpp
r88449 r88450 64 64 65 65 /********************************************************************************************************************************* 66 * Defines *67 *********************************************************************************************************************************/68 69 /** Makes DRVHOSTALSAAUDIO out of PDMIHOSTAUDIO. */70 #define PDMIHOSTAUDIO_2_DRVHOSTALSAAUDIO(pInterface) \71 ( (PDRVHOSTALSAAUDIO)((uintptr_t)pInterface - RT_UOFFSETOF(DRVHOSTALSAAUDIO, IHostAudio)) )72 73 74 /*********************************************************************************************************************************75 66 * Structures * 76 67 *********************************************************************************************************************************/ 77 78 68 /** 79 69 * Structure for maintaining an ALSA audio stream. … … 139 129 140 130 131 132 /** 133 * Closes an ALSA stream 134 * 135 * @returns VBox status code. 136 * @param pphPCM ALSA stream to close. 137 */ 138 static int alsaStreamClose(snd_pcm_t **pphPCM) 139 { 140 if (!pphPCM || !*pphPCM) 141 return VINF_SUCCESS; 142 143 int rc; 144 int rc2 = snd_pcm_close(*pphPCM); 145 if (rc2) 146 { 147 LogRel(("ALSA: Closing PCM descriptor failed: %s\n", snd_strerror(rc2))); 148 rc = VERR_GENERAL_FAILURE; /** @todo */ 149 } 150 else 151 { 152 *pphPCM = NULL; 153 rc = VINF_SUCCESS; 154 } 155 156 LogFlowFuncLeaveRC(rc); 157 return rc; 158 } 159 160 161 #ifdef DEBUG 162 static void alsaDbgErrorHandler(const char *file, int line, const char *function, 163 int err, const char *fmt, ...) 164 { 165 /** @todo Implement me! */ 166 RT_NOREF(file, line, function, err, fmt); 167 } 168 #endif 169 170 171 /** 172 * Tries to recover an ALSA stream. 173 * 174 * @returns VBox status code. 175 * @param phPCM ALSA stream handle. 176 */ 177 static int alsaStreamRecover(snd_pcm_t *phPCM) 178 { 179 AssertPtrReturn(phPCM, VERR_INVALID_POINTER); 180 181 int err = snd_pcm_prepare(phPCM); 182 if (err < 0) 183 { 184 LogFunc(("Failed to recover stream %p: %s\n", phPCM, snd_strerror(err))); 185 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 186 } 187 188 return VINF_SUCCESS; 189 } 190 191 /** 192 * Resumes an ALSA stream. 193 * 194 * @returns VBox status code. 195 * @param phPCM ALSA stream to resume. 196 */ 197 static int alsaStreamResume(snd_pcm_t *phPCM) 198 { 199 AssertPtrReturn(phPCM, VERR_INVALID_POINTER); 200 201 int err = snd_pcm_resume(phPCM); 202 if (err < 0) 203 { 204 LogFunc(("Failed to resume stream %p: %s\n", phPCM, snd_strerror(err))); 205 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 206 } 207 208 return VINF_SUCCESS; 209 } 210 211 212 /** 213 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig} 214 */ 215 static DECLCALLBACK(int) drvHostAlsaAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg) 216 { 217 RT_NOREF(pInterface); 218 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER); 219 220 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "ALSA"); 221 222 pBackendCfg->cbStreamIn = sizeof(ALSAAUDIOSTREAM); 223 pBackendCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAM); 224 225 /* Enumerate sound devices. */ 226 char **pszHints; 227 int err = snd_device_name_hint(-1 /* All cards */, "pcm", (void***)&pszHints); 228 if (err == 0) 229 { 230 char** pszHintCur = pszHints; 231 while (*pszHintCur != NULL) 232 { 233 char *pszDev = snd_device_name_get_hint(*pszHintCur, "NAME"); 234 bool fSkip = !pszDev 235 || !RTStrICmp("null", pszDev); 236 if (fSkip) 237 { 238 if (pszDev) 239 free(pszDev); 240 pszHintCur++; 241 continue; 242 } 243 244 char *pszIOID = snd_device_name_get_hint(*pszHintCur, "IOID"); 245 if (pszIOID) 246 { 247 #if 0 248 if (!RTStrICmp("input", pszIOID)) 249 250 else if (!RTStrICmp("output", pszIOID)) 251 #endif 252 } 253 else /* NULL means bidirectional, input + output. */ 254 { 255 } 256 257 LogRel2(("ALSA: Found %s device: %s\n", pszIOID ? RTStrToLower(pszIOID) : "bidirectional", pszDev)); 258 259 /* Special case for ALSAAudio. */ 260 if ( pszDev 261 && RTStrIStr("pulse", pszDev) != NULL) 262 LogRel2(("ALSA: ALSAAudio plugin in use\n")); 263 264 if (pszIOID) 265 free(pszIOID); 266 267 if (pszDev) 268 free(pszDev); 269 270 pszHintCur++; 271 } 272 273 snd_device_name_free_hint((void **)pszHints); 274 pszHints = NULL; 275 } 276 else 277 LogRel2(("ALSA: Error enumerating PCM devices: %Rrc (%d)\n", RTErrConvertFromErrno(err), err)); 278 279 /* ALSA allows exactly one input and one output used at a time for the selected device(s). */ 280 pBackendCfg->cMaxStreamsIn = 1; 281 pBackendCfg->cMaxStreamsOut = 1; 282 283 return VINF_SUCCESS; 284 } 285 286 287 /** 288 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus} 289 */ 290 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostAlsaAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir) 291 { 292 RT_NOREF(enmDir); 293 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN); 294 295 return PDMAUDIOBACKENDSTS_RUNNING; 296 } 297 298 141 299 /** 142 300 * Converts internal audio PCM properties to an ALSA PCM format. … … 228 386 } 229 387 return VINF_SUCCESS; 230 }231 232 233 /**234 * Closes an ALSA stream235 *236 * @returns VBox status code.237 * @param pphPCM ALSA stream to close.238 */239 static int alsaStreamClose(snd_pcm_t **pphPCM)240 {241 if (!pphPCM || !*pphPCM)242 return VINF_SUCCESS;243 244 int rc;245 int rc2 = snd_pcm_close(*pphPCM);246 if (rc2)247 {248 LogRel(("ALSA: Closing PCM descriptor failed: %s\n", snd_strerror(rc2)));249 rc = VERR_GENERAL_FAILURE; /** @todo */250 }251 else252 {253 *pphPCM = NULL;254 rc = VINF_SUCCESS;255 }256 257 LogFlowFuncLeaveRC(rc);258 return rc;259 388 } 260 389 … … 476 605 477 606 478 #ifdef DEBUG 479 static void alsaDbgErrorHandler(const char *file, int line, const char *function, 480 int err, const char *fmt, ...) 481 { 482 /** @todo Implement me! */ 483 RT_NOREF(file, line, function, err, fmt); 484 } 485 #endif 607 /** 608 * Creates an ALSA output stream. 609 * 610 * @returns VBox status code. 611 * @param pThis The ALSA driver instance data. 612 * @param pStreamALSA ALSA output stream to create. 613 * @param pCfgReq Requested configuration to create stream with. 614 * @param pCfgAcq Obtained configuration the stream got created 615 * with on success. 616 */ 617 static int alsaCreateStreamOut(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA, 618 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 619 { 620 snd_pcm_t *phPCM = NULL; 621 622 int rc; 623 624 do 625 { 626 ALSAAUDIOSTREAMCFG req; 627 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props); 628 req.freq = PDMAudioPropsHz(&pCfgReq->Props); 629 req.nchannels = PDMAudioPropsChannels(&pCfgReq->Props); 630 req.period_size = pCfgReq->Backend.cFramesPeriod; 631 req.buffer_size = pCfgReq->Backend.cFramesBufferSize; 632 req.threshold = pCfgReq->Backend.cFramesPreBuffering; 633 634 ALSAAUDIOSTREAMCFG obt; 635 rc = alsaStreamOpen(pThis->szDefaultOut, false /* fIn */, &req, &obt, &phPCM); 636 if (RT_FAILURE(rc)) 637 break; 638 639 rc = alsaALSAToAudioProps(&pCfgAcq->Props, obt.fmt, obt.nchannels, obt.freq); 640 if (RT_FAILURE(rc)) 641 break; 642 643 pCfgAcq->Backend.cFramesPeriod = obt.period_size; 644 pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size; 645 pCfgAcq->Backend.cFramesPreBuffering = obt.threshold; 646 647 pStreamALSA->phPCM = phPCM; 648 } 649 while (0); 650 651 if (RT_FAILURE(rc)) 652 alsaStreamClose(&phPCM); 653 654 LogFlowFuncLeaveRC(rc); 655 return rc; 656 } 657 658 659 /** 660 * Creates an ALSA input stream. 661 * 662 * @returns VBox status code. 663 * @param pThis The ALSA driver instance data. 664 * @param pStreamALSA ALSA input stream to create. 665 * @param pCfgReq Requested configuration to create stream with. 666 * @param pCfgAcq Obtained configuration the stream got created 667 * with on success. 668 */ 669 static int alsaCreateStreamIn(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA, 670 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 671 { 672 int rc; 673 674 snd_pcm_t *phPCM = NULL; 675 676 do 677 { 678 ALSAAUDIOSTREAMCFG req; 679 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props); 680 req.freq = PDMAudioPropsHz(&pCfgReq->Props); 681 req.nchannels = PDMAudioPropsChannels(&pCfgReq->Props); 682 req.period_size = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 50 /*ms*/); /** @todo Make this configurable. */ 683 req.buffer_size = req.period_size * 2; /** @todo Make this configurable. */ 684 req.threshold = req.period_size; 685 686 ALSAAUDIOSTREAMCFG obt; 687 rc = alsaStreamOpen(pThis->szDefaultIn, true /* fIn */, &req, &obt, &phPCM); 688 if (RT_FAILURE(rc)) 689 break; 690 691 rc = alsaALSAToAudioProps(&pCfgAcq->Props, obt.fmt, obt.nchannels, obt.freq); 692 if (RT_FAILURE(rc)) 693 break; 694 695 pCfgAcq->Backend.cFramesPeriod = obt.period_size; 696 pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size; 697 pCfgAcq->Backend.cFramesPreBuffering = 0; /* No pre-buffering. */ 698 699 pStreamALSA->phPCM = phPCM; 700 } 701 while (0); 702 703 if (RT_FAILURE(rc)) 704 alsaStreamClose(&phPCM); 705 706 LogFlowFuncLeaveRC(rc); 707 return rc; 708 } 709 710 711 /** 712 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate} 713 */ 714 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 715 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 716 { 717 PDRVHOSTALSAAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTALSAAUDIO, IHostAudio); 718 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 719 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 720 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); 721 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 722 723 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 724 725 int rc; 726 if (pCfgReq->enmDir == PDMAUDIODIR_IN) 727 rc = alsaCreateStreamIn( pThis, pStreamALSA, pCfgReq, pCfgAcq); 728 else 729 rc = alsaCreateStreamOut(pThis, pStreamALSA, pCfgReq, pCfgAcq); 730 if (RT_SUCCESS(rc)) 731 { 732 pStreamALSA->pCfg = PDMAudioStrmCfgDup(pCfgAcq); 733 if (!pStreamALSA->pCfg) 734 rc = VERR_NO_MEMORY; 735 } 736 737 return rc; 738 } 739 740 741 /** 742 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy} 743 */ 744 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 745 { 746 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 747 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 748 749 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 750 751 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */ 752 return VINF_SUCCESS; 753 754 int rc = alsaStreamClose(&pStreamALSA->phPCM); 755 /** @todo r=bird: It's not like we can do much with a bad status... Check 756 * what the caller does... */ 757 if (RT_SUCCESS(rc)) 758 { 759 PDMAudioStrmCfgFree(pStreamALSA->pCfg); 760 pStreamALSA->pCfg = NULL; 761 } 762 763 return rc; 764 } 765 766 767 /** 768 * Controls an ALSA input stream. 769 * 770 * @returns VBox status code. 771 * @param pStreamALSA ALSA input stream to control. 772 * @param enmStreamCmd Stream command to issue. 773 */ 774 static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd) 775 { 776 int rc = VINF_SUCCESS; 777 778 int err; 779 780 switch (enmStreamCmd) 781 { 782 case PDMAUDIOSTREAMCMD_ENABLE: 783 case PDMAUDIOSTREAMCMD_RESUME: 784 { 785 err = snd_pcm_prepare(pStreamALSA->phPCM); 786 if (err < 0) 787 { 788 LogRel(("ALSA: Error preparing input stream: %s\n", snd_strerror(err))); 789 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 790 } 791 else 792 { 793 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED); 794 795 /* Only start the PCM stream for input streams. */ 796 err = snd_pcm_start(pStreamALSA->phPCM); 797 if (err < 0) 798 { 799 LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err))); 800 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 801 } 802 } 803 804 break; 805 } 806 807 case PDMAUDIOSTREAMCMD_DISABLE: 808 { 809 err = snd_pcm_drop(pStreamALSA->phPCM); 810 if (err < 0) 811 { 812 LogRel(("ALSA: Error disabling input stream: %s\n", snd_strerror(err))); 813 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 814 } 815 break; 816 } 817 818 case PDMAUDIOSTREAMCMD_PAUSE: 819 { 820 err = snd_pcm_drop(pStreamALSA->phPCM); 821 if (err < 0) 822 { 823 LogRel(("ALSA: Error pausing input stream: %s\n", snd_strerror(err))); 824 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 825 } 826 break; 827 } 828 829 default: 830 rc = VERR_NOT_SUPPORTED; 831 break; 832 } 833 834 LogFlowFuncLeaveRC(rc); 835 return rc; 836 } 837 838 839 /** 840 * Controls an ALSA output stream. 841 * 842 * @returns VBox status code. 843 * @param pStreamALSA ALSA output stream to control. 844 * @param enmStreamCmd Stream command to issue. 845 */ 846 static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd) 847 { 848 int rc = VINF_SUCCESS; 849 850 int err; 851 852 switch (enmStreamCmd) 853 { 854 case PDMAUDIOSTREAMCMD_ENABLE: 855 case PDMAUDIOSTREAMCMD_RESUME: 856 { 857 err = snd_pcm_prepare(pStreamALSA->phPCM); 858 if (err < 0) 859 { 860 LogRel(("ALSA: Error preparing output stream: %s\n", snd_strerror(err))); 861 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 862 } 863 else 864 { 865 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED); 866 } 867 868 break; 869 } 870 871 case PDMAUDIOSTREAMCMD_DISABLE: 872 { 873 err = snd_pcm_drop(pStreamALSA->phPCM); 874 if (err < 0) 875 { 876 LogRel(("ALSA: Error disabling output stream: %s\n", snd_strerror(err))); 877 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 878 } 879 break; 880 } 881 882 case PDMAUDIOSTREAMCMD_PAUSE: 883 { 884 /** @todo shouldn't this try snd_pcm_pause first? */ 885 err = snd_pcm_drop(pStreamALSA->phPCM); 886 if (err < 0) 887 { 888 LogRel(("ALSA: Error pausing output stream: %s\n", snd_strerror(err))); 889 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 890 } 891 break; 892 } 893 894 case PDMAUDIOSTREAMCMD_DRAIN: 895 { 896 snd_pcm_state_t streamState = snd_pcm_state(pStreamALSA->phPCM); 897 Log2Func(("Stream state is: %d\n", streamState)); 898 899 if ( streamState == SND_PCM_STATE_PREPARED 900 || streamState == SND_PCM_STATE_RUNNING) 901 { 902 /** @todo r=bird: You want EMT to block here for potentially 200-300ms worth 903 * of buffer to be drained? That's a certifiably bad idea. */ 904 err = snd_pcm_nonblock(pStreamALSA->phPCM, 0); 905 if (err < 0) 906 { 907 LogRel(("ALSA: Error disabling output non-blocking mode: %s\n", snd_strerror(err))); 908 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 909 break; 910 } 911 912 err = snd_pcm_drain(pStreamALSA->phPCM); 913 if (err < 0) 914 { 915 LogRel(("ALSA: Error draining output: %s\n", snd_strerror(err))); 916 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 917 break; 918 } 919 920 err = snd_pcm_nonblock(pStreamALSA->phPCM, 1); 921 if (err < 0) 922 { 923 LogRel(("ALSA: Error re-enabling output non-blocking mode: %s\n", snd_strerror(err))); 924 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 925 } 926 } 927 break; 928 } 929 930 default: 931 rc = VERR_NOT_SUPPORTED; 932 break; 933 } 934 935 LogFlowFuncLeaveRC(rc); 936 return rc; 937 } 938 939 940 /** 941 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl} 942 */ 943 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface, 944 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd) 945 { 946 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 947 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 948 949 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 950 951 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */ 952 return VINF_SUCCESS; 953 954 int rc; 955 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN) 956 rc = alsaControlStreamIn (pStreamALSA, enmStreamCmd); 957 else 958 rc = alsaControlStreamOut(pStreamALSA, enmStreamCmd); 959 960 return rc; 961 } 962 486 963 487 964 /** … … 532 1009 } 533 1010 534 /** 535 * Tries to recover an ALSA stream. 536 * 537 * @returns VBox status code. 538 * @param phPCM ALSA stream handle. 539 */ 540 static int alsaStreamRecover(snd_pcm_t *phPCM) 541 { 542 AssertPtrReturn(phPCM, VERR_INVALID_POINTER); 543 544 int err = snd_pcm_prepare(phPCM); 545 if (err < 0) 546 { 547 LogFunc(("Failed to recover stream %p: %s\n", phPCM, snd_strerror(err))); 548 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 549 } 550 551 return VINF_SUCCESS; 552 } 553 554 /** 555 * Resumes an ALSA stream. 556 * 557 * @returns VBox status code. 558 * @param phPCM ALSA stream to resume. 559 */ 560 static int alsaStreamResume(snd_pcm_t *phPCM) 561 { 562 AssertPtrReturn(phPCM, VERR_INVALID_POINTER); 563 564 int err = snd_pcm_resume(phPCM); 565 if (err < 0) 566 { 567 LogFunc(("Failed to resume stream %p: %s\n", phPCM, snd_strerror(err))); 568 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 569 } 570 571 return VINF_SUCCESS; 572 } 1011 1012 /** 1013 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable} 1014 */ 1015 static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1016 { 1017 RT_NOREF(pInterface); 1018 1019 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 1020 1021 uint32_t cbAvail = 0; 1022 1023 snd_pcm_sframes_t cFramesAvail; 1024 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail); 1025 if (RT_SUCCESS(rc)) 1026 cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail); 1027 1028 return cbAvail; 1029 } 1030 1031 1032 /** 1033 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable} 1034 */ 1035 static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1036 { 1037 RT_NOREF(pInterface); 1038 1039 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 1040 1041 uint32_t cbAvail = 0; 1042 1043 snd_pcm_sframes_t cFramesAvail; 1044 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail); 1045 if (RT_SUCCESS(rc)) 1046 cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail); 1047 1048 return cbAvail; 1049 } 1050 1051 1052 /** 1053 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending} 1054 */ 1055 static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1056 { 1057 RT_NOREF(pInterface); 1058 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 1059 AssertPtrReturn(pStreamALSA, 0); 1060 AssertPtr(pStreamALSA->pCfg); 1061 1062 /* 1063 * This is only relevant to output streams (input streams can't have 1064 * any pending, unplayed data). 1065 */ 1066 uint32_t cbPending = 0; 1067 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_OUT) 1068 { 1069 /* 1070 * Getting the delay (in audio frames) reports the time it will take 1071 * to hear a new sample after all queued samples have been played out. 1072 * 1073 * We use snd_pcm_avail_delay instead of snd_pcm_delay here as it will 1074 * update the buffer positions, and we can use the extra value against 1075 * the buffer size to double check since the delay value may include 1076 * fixed built-in delays in the processing chain and hardware. 1077 */ 1078 snd_pcm_sframes_t cFramesAvail = 0; 1079 snd_pcm_sframes_t cFramesDelay = 0; 1080 int rc = snd_pcm_avail_delay(pStreamALSA->phPCM, &cFramesAvail, &cFramesDelay); 1081 1082 /* 1083 * We now also get the state as the pending value should be zero when 1084 * we're not in a playing state. 1085 */ 1086 snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->phPCM); 1087 switch (enmState) 1088 { 1089 case SND_PCM_STATE_RUNNING: 1090 case SND_PCM_STATE_DRAINING: 1091 if (rc >= 0) 1092 { 1093 if (cFramesAvail >= pStreamALSA->pCfg->Backend.cFramesBufferSize) 1094 cbPending = 0; 1095 else 1096 cbPending = PDMAudioPropsFramesToBytes(&pStreamALSA->pCfg->Props, cFramesDelay); 1097 } 1098 break; 1099 1100 default: 1101 break; 1102 } 1103 Log2Func(("returns %u (%#x) - cFramesBufferSize=%RU32 cFramesAvail=%ld cFramesDelay=%ld rc=%d; enmState=%s (%d) \n", 1104 cbPending, cbPending, pStreamALSA->pCfg->Backend.cFramesBufferSize, cFramesAvail, cFramesDelay, rc, 1105 snd_pcm_state_name(enmState), enmState)); 1106 } 1107 return cbPending; 1108 } 1109 1110 1111 /** 1112 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus} 1113 */ 1114 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostAlsaAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1115 { 1116 RT_NOREF(pInterface, pStream); 1117 1118 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED; 1119 } 1120 573 1121 574 1122 /** … … 814 1362 } 815 1363 816 /**817 * Creates an ALSA output stream.818 *819 * @returns VBox status code.820 * @param pThis The ALSA driver instance data.821 * @param pStreamALSA ALSA output stream to create.822 * @param pCfgReq Requested configuration to create stream with.823 * @param pCfgAcq Obtained configuration the stream got created824 * with on success.825 */826 static int alsaCreateStreamOut(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA,827 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)828 {829 snd_pcm_t *phPCM = NULL;830 831 int rc;832 833 do834 {835 ALSAAUDIOSTREAMCFG req;836 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props);837 req.freq = PDMAudioPropsHz(&pCfgReq->Props);838 req.nchannels = PDMAudioPropsChannels(&pCfgReq->Props);839 req.period_size = pCfgReq->Backend.cFramesPeriod;840 req.buffer_size = pCfgReq->Backend.cFramesBufferSize;841 req.threshold = pCfgReq->Backend.cFramesPreBuffering;842 843 ALSAAUDIOSTREAMCFG obt;844 rc = alsaStreamOpen(pThis->szDefaultOut, false /* fIn */, &req, &obt, &phPCM);845 if (RT_FAILURE(rc))846 break;847 848 rc = alsaALSAToAudioProps(&pCfgAcq->Props, obt.fmt, obt.nchannels, obt.freq);849 if (RT_FAILURE(rc))850 break;851 852 pCfgAcq->Backend.cFramesPeriod = obt.period_size;853 pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;854 pCfgAcq->Backend.cFramesPreBuffering = obt.threshold;855 856 pStreamALSA->phPCM = phPCM;857 }858 while (0);859 860 if (RT_FAILURE(rc))861 alsaStreamClose(&phPCM);862 863 LogFlowFuncLeaveRC(rc);864 return rc;865 }866 867 /**868 * Creates an ALSA input stream.869 *870 * @returns VBox status code.871 * @param pThis The ALSA driver instance data.872 * @param pStreamALSA ALSA input stream to create.873 * @param pCfgReq Requested configuration to create stream with.874 * @param pCfgAcq Obtained configuration the stream got created875 * with on success.876 */877 static int alsaCreateStreamIn(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA,878 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)879 {880 int rc;881 882 snd_pcm_t *phPCM = NULL;883 884 do885 {886 ALSAAUDIOSTREAMCFG req;887 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props);888 req.freq = PDMAudioPropsHz(&pCfgReq->Props);889 req.nchannels = PDMAudioPropsChannels(&pCfgReq->Props);890 req.period_size = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 50 /*ms*/); /** @todo Make this configurable. */891 req.buffer_size = req.period_size * 2; /** @todo Make this configurable. */892 req.threshold = req.period_size;893 894 ALSAAUDIOSTREAMCFG obt;895 rc = alsaStreamOpen(pThis->szDefaultIn, true /* fIn */, &req, &obt, &phPCM);896 if (RT_FAILURE(rc))897 break;898 899 rc = alsaALSAToAudioProps(&pCfgAcq->Props, obt.fmt, obt.nchannels, obt.freq);900 if (RT_FAILURE(rc))901 break;902 903 pCfgAcq->Backend.cFramesPeriod = obt.period_size;904 pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;905 pCfgAcq->Backend.cFramesPreBuffering = 0; /* No pre-buffering. */906 907 pStreamALSA->phPCM = phPCM;908 }909 while (0);910 911 if (RT_FAILURE(rc))912 alsaStreamClose(&phPCM);913 914 LogFlowFuncLeaveRC(rc);915 return rc;916 }917 918 /**919 * Controls an ALSA input stream.920 *921 * @returns VBox status code.922 * @param pStreamALSA ALSA input stream to control.923 * @param enmStreamCmd Stream command to issue.924 */925 static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)926 {927 int rc = VINF_SUCCESS;928 929 int err;930 931 switch (enmStreamCmd)932 {933 case PDMAUDIOSTREAMCMD_ENABLE:934 case PDMAUDIOSTREAMCMD_RESUME:935 {936 err = snd_pcm_prepare(pStreamALSA->phPCM);937 if (err < 0)938 {939 LogRel(("ALSA: Error preparing input stream: %s\n", snd_strerror(err)));940 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */941 }942 else943 {944 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);945 946 /* Only start the PCM stream for input streams. */947 err = snd_pcm_start(pStreamALSA->phPCM);948 if (err < 0)949 {950 LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err)));951 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */952 }953 }954 955 break;956 }957 958 case PDMAUDIOSTREAMCMD_DISABLE:959 {960 err = snd_pcm_drop(pStreamALSA->phPCM);961 if (err < 0)962 {963 LogRel(("ALSA: Error disabling input stream: %s\n", snd_strerror(err)));964 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */965 }966 break;967 }968 969 case PDMAUDIOSTREAMCMD_PAUSE:970 {971 err = snd_pcm_drop(pStreamALSA->phPCM);972 if (err < 0)973 {974 LogRel(("ALSA: Error pausing input stream: %s\n", snd_strerror(err)));975 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */976 }977 break;978 }979 980 default:981 rc = VERR_NOT_SUPPORTED;982 break;983 }984 985 LogFlowFuncLeaveRC(rc);986 return rc;987 }988 989 /**990 * Controls an ALSA output stream.991 *992 * @returns VBox status code.993 * @param pStreamALSA ALSA output stream to control.994 * @param enmStreamCmd Stream command to issue.995 */996 static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)997 {998 int rc = VINF_SUCCESS;999 1000 int err;1001 1002 switch (enmStreamCmd)1003 {1004 case PDMAUDIOSTREAMCMD_ENABLE:1005 case PDMAUDIOSTREAMCMD_RESUME:1006 {1007 err = snd_pcm_prepare(pStreamALSA->phPCM);1008 if (err < 0)1009 {1010 LogRel(("ALSA: Error preparing output stream: %s\n", snd_strerror(err)));1011 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */1012 }1013 else1014 {1015 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);1016 }1017 1018 break;1019 }1020 1021 case PDMAUDIOSTREAMCMD_DISABLE:1022 {1023 err = snd_pcm_drop(pStreamALSA->phPCM);1024 if (err < 0)1025 {1026 LogRel(("ALSA: Error disabling output stream: %s\n", snd_strerror(err)));1027 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */1028 }1029 break;1030 }1031 1032 case PDMAUDIOSTREAMCMD_PAUSE:1033 {1034 /** @todo shouldn't this try snd_pcm_pause first? */1035 err = snd_pcm_drop(pStreamALSA->phPCM);1036 if (err < 0)1037 {1038 LogRel(("ALSA: Error pausing output stream: %s\n", snd_strerror(err)));1039 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */1040 }1041 break;1042 }1043 1044 case PDMAUDIOSTREAMCMD_DRAIN:1045 {1046 snd_pcm_state_t streamState = snd_pcm_state(pStreamALSA->phPCM);1047 Log2Func(("Stream state is: %d\n", streamState));1048 1049 if ( streamState == SND_PCM_STATE_PREPARED1050 || streamState == SND_PCM_STATE_RUNNING)1051 {1052 /** @todo r=bird: You want EMT to block here for potentially 200-300ms worth1053 * of buffer to be drained? That's a certifiably bad idea. */1054 err = snd_pcm_nonblock(pStreamALSA->phPCM, 0);1055 if (err < 0)1056 {1057 LogRel(("ALSA: Error disabling output non-blocking mode: %s\n", snd_strerror(err)));1058 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */1059 break;1060 }1061 1062 err = snd_pcm_drain(pStreamALSA->phPCM);1063 if (err < 0)1064 {1065 LogRel(("ALSA: Error draining output: %s\n", snd_strerror(err)));1066 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */1067 break;1068 }1069 1070 err = snd_pcm_nonblock(pStreamALSA->phPCM, 1);1071 if (err < 0)1072 {1073 LogRel(("ALSA: Error re-enabling output non-blocking mode: %s\n", snd_strerror(err)));1074 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */1075 }1076 }1077 break;1078 }1079 1080 default:1081 rc = VERR_NOT_SUPPORTED;1082 break;1083 }1084 1085 LogFlowFuncLeaveRC(rc);1086 return rc;1087 }1088 1089 1090 /**1091 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}1092 */1093 static DECLCALLBACK(int) drvHostAlsaAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)1094 {1095 RT_NOREF(pInterface);1096 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);1097 1098 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "ALSA");1099 1100 pBackendCfg->cbStreamIn = sizeof(ALSAAUDIOSTREAM);1101 pBackendCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAM);1102 1103 /* Enumerate sound devices. */1104 char **pszHints;1105 int err = snd_device_name_hint(-1 /* All cards */, "pcm", (void***)&pszHints);1106 if (err == 0)1107 {1108 char** pszHintCur = pszHints;1109 while (*pszHintCur != NULL)1110 {1111 char *pszDev = snd_device_name_get_hint(*pszHintCur, "NAME");1112 bool fSkip = !pszDev1113 || !RTStrICmp("null", pszDev);1114 if (fSkip)1115 {1116 if (pszDev)1117 free(pszDev);1118 pszHintCur++;1119 continue;1120 }1121 1122 char *pszIOID = snd_device_name_get_hint(*pszHintCur, "IOID");1123 if (pszIOID)1124 {1125 #if 01126 if (!RTStrICmp("input", pszIOID))1127 1128 else if (!RTStrICmp("output", pszIOID))1129 #endif1130 }1131 else /* NULL means bidirectional, input + output. */1132 {1133 }1134 1135 LogRel2(("ALSA: Found %s device: %s\n", pszIOID ? RTStrToLower(pszIOID) : "bidirectional", pszDev));1136 1137 /* Special case for ALSAAudio. */1138 if ( pszDev1139 && RTStrIStr("pulse", pszDev) != NULL)1140 LogRel2(("ALSA: ALSAAudio plugin in use\n"));1141 1142 if (pszIOID)1143 free(pszIOID);1144 1145 if (pszDev)1146 free(pszDev);1147 1148 pszHintCur++;1149 }1150 1151 snd_device_name_free_hint((void **)pszHints);1152 pszHints = NULL;1153 }1154 else1155 LogRel2(("ALSA: Error enumerating PCM devices: %Rrc (%d)\n", RTErrConvertFromErrno(err), err));1156 1157 /* ALSA allows exactly one input and one output used at a time for the selected device(s). */1158 pBackendCfg->cMaxStreamsIn = 1;1159 pBackendCfg->cMaxStreamsOut = 1;1160 1161 return VINF_SUCCESS;1162 }1163 1164 1165 /**1166 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}1167 */1168 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostAlsaAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)1169 {1170 RT_NOREF(enmDir);1171 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);1172 1173 return PDMAUDIOBACKENDSTS_RUNNING;1174 }1175 1176 1177 /**1178 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}1179 */1180 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,1181 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)1182 {1183 PDRVHOSTALSAAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTALSAAUDIO, IHostAudio);1184 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);1185 AssertPtrReturn(pStream, VERR_INVALID_POINTER);1186 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);1187 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);1188 1189 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;1190 1191 int rc;1192 if (pCfgReq->enmDir == PDMAUDIODIR_IN)1193 rc = alsaCreateStreamIn( pThis, pStreamALSA, pCfgReq, pCfgAcq);1194 else1195 rc = alsaCreateStreamOut(pThis, pStreamALSA, pCfgReq, pCfgAcq);1196 1197 if (RT_SUCCESS(rc))1198 {1199 pStreamALSA->pCfg = PDMAudioStrmCfgDup(pCfgAcq);1200 if (!pStreamALSA->pCfg)1201 rc = VERR_NO_MEMORY;1202 }1203 1204 return rc;1205 }1206 1207 1208 /**1209 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}1210 */1211 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)1212 {1213 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);1214 AssertPtrReturn(pStream, VERR_INVALID_POINTER);1215 1216 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;1217 1218 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */1219 return VINF_SUCCESS;1220 1221 int rc = alsaStreamClose(&pStreamALSA->phPCM);1222 /** @todo r=bird: It's not like we can do much with a bad status... Check1223 * what the caller does... */1224 if (RT_SUCCESS(rc))1225 {1226 PDMAudioStrmCfgFree(pStreamALSA->pCfg);1227 pStreamALSA->pCfg = NULL;1228 }1229 1230 return rc;1231 }1232 1233 1234 /**1235 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}1236 */1237 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,1238 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)1239 {1240 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);1241 AssertPtrReturn(pStream, VERR_INVALID_POINTER);1242 1243 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;1244 1245 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */1246 return VINF_SUCCESS;1247 1248 int rc;1249 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN)1250 rc = alsaControlStreamIn (pStreamALSA, enmStreamCmd);1251 else1252 rc = alsaControlStreamOut(pStreamALSA, enmStreamCmd);1253 1254 return rc;1255 }1256 1257 1258 /**1259 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}1260 */1261 static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)1262 {1263 RT_NOREF(pInterface);1264 1265 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;1266 1267 uint32_t cbAvail = 0;1268 1269 snd_pcm_sframes_t cFramesAvail;1270 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);1271 if (RT_SUCCESS(rc))1272 cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);1273 1274 return cbAvail;1275 }1276 1277 1278 /**1279 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}1280 */1281 static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)1282 {1283 RT_NOREF(pInterface);1284 1285 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;1286 1287 uint32_t cbAvail = 0;1288 1289 snd_pcm_sframes_t cFramesAvail;1290 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);1291 if (RT_SUCCESS(rc))1292 cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);1293 1294 return cbAvail;1295 }1296 1297 1298 /**1299 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}1300 */1301 static DECLCALLBACK(uint32_t) drvHostALSAStreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)1302 {1303 RT_NOREF(pInterface);1304 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;1305 AssertPtrReturn(pStreamALSA, 0);1306 AssertPtr(pStreamALSA->pCfg);1307 1308 /*1309 * This is only relevant to output streams (input streams can't have1310 * any pending, unplayed data).1311 */1312 uint32_t cbPending = 0;1313 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_OUT)1314 {1315 /*1316 * Getting the delay (in audio frames) reports the time it will take1317 * to hear a new sample after all queued samples have been played out.1318 *1319 * We use snd_pcm_avail_delay instead of snd_pcm_delay here as it will1320 * update the buffer positions, and we can use the extra value against1321 * the buffer size to double check since the delay value may include1322 * fixed built-in delays in the processing chain and hardware.1323 */1324 snd_pcm_sframes_t cFramesAvail = 0;1325 snd_pcm_sframes_t cFramesDelay = 0;1326 int rc = snd_pcm_avail_delay(pStreamALSA->phPCM, &cFramesAvail, &cFramesDelay);1327 1328 /*1329 * We now also get the state as the pending value should be zero when1330 * we're not in a playing state.1331 */1332 snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->phPCM);1333 switch (enmState)1334 {1335 case SND_PCM_STATE_RUNNING:1336 case SND_PCM_STATE_DRAINING:1337 if (rc >= 0)1338 {1339 if (cFramesAvail >= pStreamALSA->pCfg->Backend.cFramesBufferSize)1340 cbPending = 0;1341 else1342 cbPending = PDMAudioPropsFramesToBytes(&pStreamALSA->pCfg->Props, cFramesDelay);1343 }1344 break;1345 1346 default:1347 break;1348 }1349 Log2Func(("returns %u (%#x) - cFramesBufferSize=%RU32 cFramesAvail=%ld cFramesDelay=%ld rc=%d; enmState=%s (%d) \n",1350 cbPending, cbPending, pStreamALSA->pCfg->Backend.cFramesBufferSize, cFramesAvail, cFramesDelay, rc,1351 snd_pcm_state_name(enmState), enmState));1352 }1353 return cbPending;1354 }1355 1356 1357 /**1358 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}1359 */1360 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostAlsaAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)1361 {1362 RT_NOREF(pInterface, pStream);1363 1364 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;1365 }1366 1367 1364 1368 1365 /** … … 1400 1397 /* IHostAudio */ 1401 1398 pThis->IHostAudio.pfnGetConfig = drvHostAlsaAudioHA_GetConfig; 1399 pThis->IHostAudio.pfnGetDevices = NULL; 1402 1400 pThis->IHostAudio.pfnGetStatus = drvHostAlsaAudioHA_GetStatus; 1403 1401 pThis->IHostAudio.pfnStreamCreate = drvHostAlsaAudioHA_StreamCreate; … … 1406 1404 pThis->IHostAudio.pfnStreamGetReadable = drvHostAlsaAudioHA_StreamGetReadable; 1407 1405 pThis->IHostAudio.pfnStreamGetWritable = drvHostAlsaAudioHA_StreamGetWritable; 1406 pThis->IHostAudio.pfnStreamGetPending = drvHostAlsaAudioHA_StreamGetPending; 1408 1407 pThis->IHostAudio.pfnStreamGetStatus = drvHostAlsaAudioHA_StreamGetStatus; 1409 1408 pThis->IHostAudio.pfnStreamPlay = drvHostAlsaAudioHA_StreamPlay; 1410 1409 pThis->IHostAudio.pfnStreamCapture = drvHostAlsaAudioHA_StreamCapture; 1411 pThis->IHostAudio.pfnGetDevices = NULL;1412 pThis->IHostAudio.pfnStreamGetPending = drvHostALSAStreamGetPending;1413 1410 1414 1411 /*
Note:
See TracChangeset
for help on using the changeset viewer.