VirtualBox

Ignore:
Timestamp:
Jul 26, 2018 1:52:12 PM (6 years ago)
Author:
vboxsync
Message:

Audio: Implemented backend-independent (pre-)buffering support. Work in progress.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp

    r73097 r73370  
    123123static ALSAAUDIOCFG s_ALSAConf =
    124124{
    125 #ifdef HIGH_LATENCY
    126     1,
    127     1,
    128 #else
    129125    0,
    130126    0,
    131 #endif
    132127    "default",
    133128    "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. */
    148136    0,
    149137    0,
     
    180168    int                 resample;
    181169    int                 nchannels;
     170    /** Buffer size (in audio frames). */
    182171    unsigned long       buffer_size;
     172    /** Periods (in audio frames). */
    183173    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;
    185177} ALSAAUDIOSTREAMCFG, *PALSAAUDIOSTREAMCFG;
    186178
     
    288280
    289281
    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 
    324282static int alsaStreamSetThreshold(snd_pcm_t *phPCM, snd_pcm_uframes_t threshold)
    325283{
     
    395353    }
    396354
     355    LogFlowFuncLeaveRC(rc);
    397356    return rc;
    398357}
     
    612571        }
    613572
     573        LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev));
     574
    614575        int err = snd_pcm_open(&phPCM, pszDev,
    615576                               fIn ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
     
    622583        }
    623584
    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        }
    625592
    626593        snd_pcm_hw_params_t *pHWParms;
     
    683650            if (!buffer_size)
    684651            {
    685                 buffer_size = DEFAULT_BUFFER_SIZE;
    686                 period_size = DEFAULT_PERIOD_SIZE;
     652                buffer_size = DEFAULT_BUFFER_FRAMES;
     653                period_size = DEFAULT_PERIOD_FRAMES;
    687654            }
    688655        }
     
    792759                err = snd_pcm_hw_params_set_buffer_size_near(phPCM,
    793760                                                             pHWParms, &buffer_size_f);
    794                 LogFunc(("Buffer size is: %RU32\n", buffer_size_f));
    795761                if (err < 0)
    796762                {
    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)));
    799764                    rc = VERR_AUDIO_BACKEND_INIT_FAILED;
    800765                    break;
     
    821786        }
    822787
    823         LogFunc(("Buffer sample size is: %RU32\n", obt_buffer_size));
    824 
    825788        snd_pcm_uframes_t obt_period_size;
    826789        int dir = 0;
     
    833796        }
    834797
    835         LogFunc(("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",
    836799                 pCfgReq->freq, obt_period_size, obt_buffer_size));
    837800
     
    845808
    846809        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;
    865827    }
    866828    while (0);
     
    868830    if (RT_SUCCESS(rc))
    869831    {
    870         pCfgObt->fmt       = pCfgReq->fmt;
    871         pCfgObt->nchannels = cChannels;
    872         pCfgObt->freq      = uFreq;
    873         pCfgObt->samples   = obt_buffer_size;
    874 
    875832        *pphPCM = phPCM;
    876833    }
     
    953910
    954911    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     else
    975     {
    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         else
    983         {
    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;
    999912}
    1000913
     
    13241237        req.freq        = pCfgReq->Props.uHz;
    13251238        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;
    13281242
    13291243        ALSAAUDIOSTREAMCFG obt;
     
    13391253            break;
    13401254
    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);
    13491261        if (!pStreamALSA->pvBuf)
    13501262        {
    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));
    13521264            rc = VERR_NO_MEMORY;
    13531265            break;
    13541266        }
    13551267
    1356         pStreamALSA->cbBuf = cbBuf;
    13571268        pStreamALSA->phPCM = phPCM;
    13581269    }
     
    13791290        req.freq        = pCfgReq->Props.uHz;
    13801291        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;
    13831295
    13841296        ALSAAUDIOSTREAMCFG obt;
     
    13941306            break;
    13951307
    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);
    14041314        if (!pStreamALSA->pvBuf)
    14051315        {
    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));
    14071317            rc = VERR_NO_MEMORY;
    14081318            break;
    14091319        }
    14101320
    1411         pStreamALSA->cbBuf = cbBuf;
    14121321        pStreamALSA->phPCM = phPCM;
    14131322    }
     
    14241333static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
    14251334{
    1426     int rc;
     1335    int rc = VINF_SUCCESS;
     1336
     1337    int err;
     1338
    14271339    switch (enmStreamCmd)
    14281340    {
    14291341        case PDMAUDIOSTREAMCMD_ENABLE:
    14301342        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        }
    14331365
    14341366        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
    14351377        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        }
    14381387
    14391388        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);
    14451394    return rc;
    14461395}
     
    14491398static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
    14501399{
    1451     int rc;
     1400    int rc = VINF_SUCCESS;
     1401
     1402    int err;
     1403
    14521404    switch (enmStreamCmd)
    14531405    {
    14541406        case PDMAUDIOSTREAMCMD_ENABLE:
    14551407        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        }
    14581422
    14591423        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
    14601434        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        }
    14631478
    14641479        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);
    14701485    return rc;
    14711486}
     
    16931708
    16941709/**
     1710 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
     1711 */
     1712static 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/**
    16951746 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
    16961747 */
     
    17521803    /* IHostAudio */
    17531804    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostALSAAudio);
     1805    pThis->IHostAudio.pfnStreamGetPending = drvHostALSAStreamGetPending;
    17541806
    17551807    return VINF_SUCCESS;
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette