Changeset 73370 in vbox for trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp
- Timestamp:
- Jul 26, 2018 1:52:12 PM (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp
r73097 r73370 123 123 static ALSAAUDIOCFG s_ALSAConf = 124 124 { 125 #ifdef HIGH_LATENCY126 1,127 1,128 #else129 125 0, 130 126 0, 131 #endif132 127 "default", 133 128 "default", 134 #ifdef HIGH_LATENCY 135 400000, 136 400000 / 4, 137 400000, 138 400000 / 4, 139 #else 140 # define DEFAULT_BUFFER_SIZE 1024 141 # define DEFAULT_PERIOD_SIZE 256 142 DEFAULT_BUFFER_SIZE * 4, 143 DEFAULT_PERIOD_SIZE * 4, 144 DEFAULT_BUFFER_SIZE, 145 DEFAULT_PERIOD_SIZE, 146 #endif 147 0, 129 # define DEFAULT_PERIOD_FRAMES 4410 /* 100ms for 44,1 kHz. */ 130 # define DEFAULT_BUFFER_FRAMES DEFAULT_PERIOD_FRAMES * 2 /* Double (buffering) the period size. */ 131 DEFAULT_BUFFER_FRAMES, 132 DEFAULT_PERIOD_FRAMES, 133 DEFAULT_BUFFER_FRAMES, 134 DEFAULT_PERIOD_FRAMES, 135 DEFAULT_PERIOD_FRAMES * 2, /* Threshold, using double the period size to avoid stutters. */ 148 136 0, 149 137 0, … … 180 168 int resample; 181 169 int nchannels; 170 /** Buffer size (in audio frames). */ 182 171 unsigned long buffer_size; 172 /** Periods (in audio frames). */ 183 173 unsigned long period_size; 184 snd_pcm_uframes_t samples; 174 /** For playback: Starting to play threshold (in audio frames). 175 * For Capturing: Starting to capture threshold (in audio frames). */ 176 unsigned long threshold; 185 177 } ALSAAUDIOSTREAMCFG, *PALSAAUDIOSTREAMCFG; 186 178 … … 288 280 289 281 290 static int alsaGetSampleShift(snd_pcm_format_t fmt, unsigned *puShift)291 {292 AssertPtrReturn(puShift, VERR_INVALID_POINTER);293 294 switch (fmt)295 {296 case SND_PCM_FORMAT_S8:297 case SND_PCM_FORMAT_U8:298 *puShift = 0;299 break;300 301 case SND_PCM_FORMAT_S16_LE:302 case SND_PCM_FORMAT_U16_LE:303 case SND_PCM_FORMAT_S16_BE:304 case SND_PCM_FORMAT_U16_BE:305 *puShift = 1;306 break;307 308 case SND_PCM_FORMAT_S32_LE:309 case SND_PCM_FORMAT_U32_LE:310 case SND_PCM_FORMAT_S32_BE:311 case SND_PCM_FORMAT_U32_BE:312 *puShift = 2;313 break;314 315 default:316 AssertMsgFailed(("Format %ld not supported\n", fmt));317 return VERR_NOT_SUPPORTED;318 }319 320 return VINF_SUCCESS;321 }322 323 324 282 static int alsaStreamSetThreshold(snd_pcm_t *phPCM, snd_pcm_uframes_t threshold) 325 283 { … … 395 353 } 396 354 355 LogFlowFuncLeaveRC(rc); 397 356 return rc; 398 357 } … … 612 571 } 613 572 573 LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev)); 574 614 575 int err = snd_pcm_open(&phPCM, pszDev, 615 576 fIn ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, … … 622 583 } 623 584 624 LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev)); 585 err = snd_pcm_nonblock(phPCM, 1); 586 if (err < 0) 587 { 588 LogRel(("ALSA: Error setting output non-blocking mode: %s\n", snd_strerror(err))); 589 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 590 break; 591 } 625 592 626 593 snd_pcm_hw_params_t *pHWParms; … … 683 650 if (!buffer_size) 684 651 { 685 buffer_size = DEFAULT_BUFFER_ SIZE;686 period_size = DEFAULT_PERIOD_ SIZE;652 buffer_size = DEFAULT_BUFFER_FRAMES; 653 period_size = DEFAULT_PERIOD_FRAMES; 687 654 } 688 655 } … … 792 759 err = snd_pcm_hw_params_set_buffer_size_near(phPCM, 793 760 pHWParms, &buffer_size_f); 794 LogFunc(("Buffer size is: %RU32\n", buffer_size_f));795 761 if (err < 0) 796 762 { 797 LogRel(("ALSA: Failed to set buffer size %d: %s\n", 798 buffer_size_f, snd_strerror(err))); 763 LogRel(("ALSA: Failed to set near buffer size %RU32: %s\n", buffer_size_f, snd_strerror(err))); 799 764 rc = VERR_AUDIO_BACKEND_INIT_FAILED; 800 765 break; … … 821 786 } 822 787 823 LogFunc(("Buffer sample size is: %RU32\n", obt_buffer_size));824 825 788 snd_pcm_uframes_t obt_period_size; 826 789 int dir = 0; … … 833 796 } 834 797 835 Log Func(("Freq=%dHz, period size=%RU32, buffer size=%RU32\n",798 LogRel2(("ALSA: Frequency is %dHz, period size is %RU32 frames, buffer size is %RU32 fames\n", 836 799 pCfgReq->freq, obt_period_size, obt_buffer_size)); 837 800 … … 845 808 846 809 if ( !fIn 847 && s_ALSAConf.threshold) 848 { 849 unsigned uShift; 850 rc = alsaGetSampleShift(pCfgReq->fmt, &uShift); 851 if (RT_SUCCESS(rc)) 852 { 853 int bytes_per_sec = uFreq 854 << (cChannels == 2) 855 << uShift; 856 857 snd_pcm_uframes_t threshold 858 = (s_ALSAConf.threshold * bytes_per_sec) / 1000; 859 860 rc = alsaStreamSetThreshold(phPCM, threshold); 861 } 862 } 863 else 864 rc = VINF_SUCCESS; 810 && pCfgReq->threshold) 811 { 812 rc = alsaStreamSetThreshold(phPCM, pCfgReq->threshold); 813 if (RT_FAILURE(rc)) 814 break; 815 816 LogRel2(("ALSA: Threshold for playback set to %RU32 frames\n", pCfgReq->threshold)); 817 } 818 819 pCfgObt->fmt = pCfgReq->fmt; 820 pCfgObt->nchannels = cChannels; 821 pCfgObt->freq = uFreq; 822 pCfgObt->period_size = obt_period_size; 823 pCfgObt->buffer_size = obt_buffer_size; 824 pCfgObt->threshold = pCfgReq->threshold; 825 826 rc = VINF_SUCCESS; 865 827 } 866 828 while (0); … … 868 830 if (RT_SUCCESS(rc)) 869 831 { 870 pCfgObt->fmt = pCfgReq->fmt;871 pCfgObt->nchannels = cChannels;872 pCfgObt->freq = uFreq;873 pCfgObt->samples = obt_buffer_size;874 875 832 *pphPCM = phPCM; 876 833 } … … 953 910 954 911 return VINF_SUCCESS; 955 }956 957 958 static int drvHostALSAAudioStreamCtl(PALSAAUDIOSTREAM pStreamALSA, bool fPause)959 {960 int rc = VINF_SUCCESS;961 962 const bool fInput = pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN;963 964 int err;965 if (fPause)966 {967 err = snd_pcm_drop(pStreamALSA->phPCM);968 if (err < 0)969 {970 LogRel(("ALSA: Error stopping %s stream: %s\n", fInput ? "input" : "output", snd_strerror(err)));971 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */972 }973 }974 else975 {976 err = snd_pcm_prepare(pStreamALSA->phPCM);977 if (err < 0)978 {979 LogRel(("ALSA: Error preparing %s stream: %s\n", fInput ? "input" : "output", snd_strerror(err)));980 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */981 }982 else983 {984 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);985 986 if (fInput) /* Only start the PCM stream for input streams. */987 {988 err = snd_pcm_start(pStreamALSA->phPCM);989 if (err < 0)990 {991 LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err)));992 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */993 }994 }995 }996 }997 998 return rc;999 912 } 1000 913 … … 1324 1237 req.freq = pCfgReq->Props.uHz; 1325 1238 req.nchannels = pCfgReq->Props.cChannels; 1326 req.period_size = s_ALSAConf.period_size_out; /** @todo Make this configurable. */ 1327 req.buffer_size = s_ALSAConf.buffer_size_out; /** @todo Make this configurable. */ 1239 req.period_size = pCfgReq->Backend.cfPeriod; 1240 req.buffer_size = pCfgReq->Backend.cfBufferSize; 1241 req.threshold = pCfgReq->Backend.cfPreBuf; 1328 1242 1329 1243 ALSAAUDIOSTREAMCFG obt; … … 1339 1253 break; 1340 1254 1341 pCfgAcq->cFrameBufferHint = obt.samples * 4; 1342 1343 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER); 1344 1345 size_t cbBuf = obt.samples * PDMAUDIOSTREAMCFG_F2B(pCfgAcq, 1); 1346 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER); 1347 1348 pStreamALSA->pvBuf = RTMemAllocZ(cbBuf); 1255 pCfgAcq->Backend.cfPeriod = obt.period_size; 1256 pCfgAcq->Backend.cfBufferSize = obt.buffer_size; 1257 pCfgAcq->Backend.cfPreBuf = obt.threshold; 1258 1259 pStreamALSA->cbBuf = pCfgAcq->Backend.cfBufferSize * DrvAudioHlpPCMPropsBytesPerFrame(&pCfgAcq->Props); 1260 pStreamALSA->pvBuf = RTMemAllocZ(pStreamALSA->cbBuf); 1349 1261 if (!pStreamALSA->pvBuf) 1350 1262 { 1351 LogRel(("ALSA: Not enough memory for output DAC buffer (% RU32 samples, %zu bytes)\n", obt.samples, cbBuf));1263 LogRel(("ALSA: Not enough memory for output DAC buffer (%zu frames)\n", pCfgAcq->Backend.cfBufferSize)); 1352 1264 rc = VERR_NO_MEMORY; 1353 1265 break; 1354 1266 } 1355 1267 1356 pStreamALSA->cbBuf = cbBuf;1357 1268 pStreamALSA->phPCM = phPCM; 1358 1269 } … … 1379 1290 req.freq = pCfgReq->Props.uHz; 1380 1291 req.nchannels = pCfgReq->Props.cChannels; 1381 req.period_size = s_ALSAConf.period_size_in; /** @todo Make this configurable. */ 1382 req.buffer_size = s_ALSAConf.buffer_size_in; /** @todo Make this configurable. */ 1292 req.period_size = DrvAudioHlpMsToFrames(&pCfgReq->Props, 50 /* ms */); /** @todo Make this configurable. */ 1293 req.buffer_size = req.period_size * 2; /** @todo Make this configurable. */ 1294 req.threshold = req.period_size; 1383 1295 1384 1296 ALSAAUDIOSTREAMCFG obt; … … 1394 1306 break; 1395 1307 1396 pCfgAcq->cFrameBufferHint = obt.samples; 1397 1398 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER); 1399 1400 size_t cbBuf = obt.samples * PDMAUDIOSTREAMCFG_F2B(pCfgAcq, 1); 1401 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER); 1402 1403 pStreamALSA->pvBuf = RTMemAlloc(cbBuf); 1308 pCfgAcq->Backend.cfPeriod = obt.period_size; 1309 pCfgAcq->Backend.cfBufferSize = obt.buffer_size; 1310 /* No pre-buffering. */ 1311 1312 pStreamALSA->cbBuf = pCfgAcq->Backend.cfBufferSize * DrvAudioHlpPCMPropsBytesPerFrame(&pCfgAcq->Props); 1313 pStreamALSA->pvBuf = RTMemAlloc(pStreamALSA->cbBuf); 1404 1314 if (!pStreamALSA->pvBuf) 1405 1315 { 1406 LogRel(("ALSA: Not enough memory for input ADC buffer (% RU32 samples, %zu bytes)\n", obt.samples, cbBuf));1316 LogRel(("ALSA: Not enough memory for input ADC buffer (%zu frames)\n", pCfgAcq->Backend.cfBufferSize)); 1407 1317 rc = VERR_NO_MEMORY; 1408 1318 break; 1409 1319 } 1410 1320 1411 pStreamALSA->cbBuf = cbBuf;1412 1321 pStreamALSA->phPCM = phPCM; 1413 1322 } … … 1424 1333 static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd) 1425 1334 { 1426 int rc; 1335 int rc = VINF_SUCCESS; 1336 1337 int err; 1338 1427 1339 switch (enmStreamCmd) 1428 1340 { 1429 1341 case PDMAUDIOSTREAMCMD_ENABLE: 1430 1342 case PDMAUDIOSTREAMCMD_RESUME: 1431 rc = drvHostALSAAudioStreamCtl(pStreamALSA, false /* fStop */); 1432 break; 1343 { 1344 err = snd_pcm_prepare(pStreamALSA->phPCM); 1345 if (err < 0) 1346 { 1347 LogRel(("ALSA: Error preparing input stream: %s\n", snd_strerror(err))); 1348 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1349 } 1350 else 1351 { 1352 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED); 1353 1354 /* Only start the PCM stream for input streams. */ 1355 err = snd_pcm_start(pStreamALSA->phPCM); 1356 if (err < 0) 1357 { 1358 LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err))); 1359 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1360 } 1361 } 1362 1363 break; 1364 } 1433 1365 1434 1366 case PDMAUDIOSTREAMCMD_DISABLE: 1367 { 1368 err = snd_pcm_drop(pStreamALSA->phPCM); 1369 if (err < 0) 1370 { 1371 LogRel(("ALSA: Error disabling input %s stream: %s\n", snd_strerror(err))); 1372 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1373 } 1374 break; 1375 } 1376 1435 1377 case PDMAUDIOSTREAMCMD_PAUSE: 1436 rc = drvHostALSAAudioStreamCtl(pStreamALSA, true /* fStop */); 1437 break; 1378 { 1379 err = snd_pcm_drop(pStreamALSA->phPCM); 1380 if (err < 0) 1381 { 1382 LogRel(("ALSA: Error pausing input stream: %s\n", snd_strerror(err))); 1383 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1384 } 1385 break; 1386 } 1438 1387 1439 1388 default: 1440 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));1441 rc = VERR_INVALID_PARAMETER;1442 break;1443 } 1444 1389 rc = VERR_NOT_SUPPORTED; 1390 break; 1391 } 1392 1393 LogFlowFuncLeaveRC(rc); 1445 1394 return rc; 1446 1395 } … … 1449 1398 static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd) 1450 1399 { 1451 int rc; 1400 int rc = VINF_SUCCESS; 1401 1402 int err; 1403 1452 1404 switch (enmStreamCmd) 1453 1405 { 1454 1406 case PDMAUDIOSTREAMCMD_ENABLE: 1455 1407 case PDMAUDIOSTREAMCMD_RESUME: 1456 rc = drvHostALSAAudioStreamCtl(pStreamALSA, false /* fStop */); 1457 break; 1408 { 1409 err = snd_pcm_prepare(pStreamALSA->phPCM); 1410 if (err < 0) 1411 { 1412 LogRel(("ALSA: Error preparing output stream: %s\n", snd_strerror(err))); 1413 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1414 } 1415 else 1416 { 1417 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED); 1418 } 1419 1420 break; 1421 } 1458 1422 1459 1423 case PDMAUDIOSTREAMCMD_DISABLE: 1424 { 1425 err = snd_pcm_drop(pStreamALSA->phPCM); 1426 if (err < 0) 1427 { 1428 LogRel(("ALSA: Error disabling output stream: %s\n", snd_strerror(err))); 1429 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1430 } 1431 break; 1432 } 1433 1460 1434 case PDMAUDIOSTREAMCMD_PAUSE: 1461 rc = drvHostALSAAudioStreamCtl(pStreamALSA, true /* fStop */); 1462 break; 1435 { 1436 err = snd_pcm_drop(pStreamALSA->phPCM); 1437 if (err < 0) 1438 { 1439 LogRel(("ALSA: Error pausing output stream: %s\n", snd_strerror(err))); 1440 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1441 } 1442 break; 1443 } 1444 1445 case PDMAUDIOSTREAMCMD_DRAIN: 1446 { 1447 snd_pcm_state_t streamState = snd_pcm_state(pStreamALSA->phPCM); 1448 Log2Func(("Stream state is: %d\n", streamState)); 1449 1450 if ( streamState == SND_PCM_STATE_PREPARED 1451 || streamState == SND_PCM_STATE_RUNNING) 1452 { 1453 err = snd_pcm_nonblock(pStreamALSA->phPCM, 0); 1454 if (err < 0) 1455 { 1456 LogRel(("ALSA: Error disabling output non-blocking mode: %s\n", snd_strerror(err))); 1457 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1458 break; 1459 } 1460 1461 err = snd_pcm_drain(pStreamALSA->phPCM); 1462 if (err < 0) 1463 { 1464 LogRel(("ALSA: Error draining output: %s\n", snd_strerror(err))); 1465 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1466 break; 1467 } 1468 1469 err = snd_pcm_nonblock(pStreamALSA->phPCM, 1); 1470 if (err < 0) 1471 { 1472 LogRel(("ALSA: Error re-enabling output non-blocking mode: %s\n", snd_strerror(err))); 1473 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 1474 } 1475 } 1476 break; 1477 } 1463 1478 1464 1479 default: 1465 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));1466 rc = VERR_INVALID_PARAMETER;1467 break;1468 } 1469 1480 rc = VERR_NOT_SUPPORTED; 1481 break; 1482 } 1483 1484 LogFlowFuncLeaveRC(rc); 1470 1485 return rc; 1471 1486 } … … 1693 1708 1694 1709 /** 1710 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending} 1711 */ 1712 static DECLCALLBACK(uint32_t) drvHostALSAStreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1713 { 1714 RT_NOREF(pInterface); 1715 AssertPtrReturn(pStream, 0); 1716 1717 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 1718 1719 snd_pcm_sframes_t cfDelay = 0; 1720 snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->phPCM); 1721 1722 int rc = VINF_SUCCESS; 1723 1724 AssertPtr(pStreamALSA->pCfg); 1725 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_OUT) 1726 { 1727 int rc2 = snd_pcm_delay(pStreamALSA->phPCM, &cfDelay); 1728 if (RT_SUCCESS(rc)) 1729 rc = rc2; 1730 1731 /* Make sure to check the stream's status. 1732 * If it's anything but SND_PCM_STATE_RUNNING, the delay is meaningless and therefore 0. */ 1733 if (enmState != SND_PCM_STATE_RUNNING) 1734 cfDelay = 0; 1735 } 1736 1737 /* Note: For input streams we never have pending data left. */ 1738 1739 Log2Func(("cfDelay=%RI32, enmState=%d, rc=%d\n", cfDelay, enmState, rc)); 1740 1741 return DrvAudioHlpFramesToBytes(&pStreamALSA->pCfg->Props, cfDelay); 1742 } 1743 1744 1745 /** 1695 1746 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus} 1696 1747 */ … … 1752 1803 /* IHostAudio */ 1753 1804 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostALSAAudio); 1805 pThis->IHostAudio.pfnStreamGetPending = drvHostALSAStreamGetPending; 1754 1806 1755 1807 return VINF_SUCCESS;
Note:
See TracChangeset
for help on using the changeset viewer.