VirtualBox

Ignore:
Timestamp:
Feb 26, 2024 12:53:47 PM (9 months ago)
Author:
vboxsync
Message:

Audio/VKAT: Use a circular buffer when recording data; should make it easier to detect and handle start/stop beacons.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ValidationKit/utils/audio/vkatCommon.cpp

    r103530 r103567  
    6666#endif
    6767
     68#include <iprt/circbuf.h>
    6869#include <iprt/ctype.h>
    6970#include <iprt/dir.h>
     
    794795
    795796        if (cbWrittenTotal != cbToWriteTotal)
    796             RTTestFailed(g_hTest, "Test #%RU32: Playback ended unexpectedly (%RU32/%RU32 played)\n",
     797        {
     798            RTTestFailed(g_hTest, "Test #%RU32: Playback ended unexpectedly (%RU32 played, expected %RU32)\n",
    797799                         idxTest, cbWrittenTotal, cbToWriteTotal);
     800            rc = cbWrittenTotal > cbToWriteTotal ? VERR_BUFFER_OVERFLOW : VERR_BUFFER_UNDERFLOW;
     801        }
    798802
    799803        if (RT_SUCCESS(rc))
     
    850854    if (RT_SUCCESS(rc))
    851855    {
    852         uint32_t cbRecTotal  = 0; /* Counts everything, including silence / whatever. */
    853         uint32_t cbTestToRec = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, pParms->msDuration);
    854         uint32_t cbTestRec   = 0;
     856        uint32_t cbToReadTotal = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, pParms->msDuration);
     857        AssertStmt(cbToReadTotal, rc = VERR_INVALID_PARAMETER);
     858        uint32_t cbReadTotal   = 0; /* Counts the read test tone data (w/o any beacons). */
    855859
    856860        RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recording %RU32 bytes total (%RU32ms timeout)\n",
    857                      idxTest, cbTestToRec, pTstEnv->msTimeout);
     861                     idxTest, cbToReadTotal, pTstEnv->msTimeout);
    858862
    859863        /* Failsafe if invalid timeout is set. */
     
    885889        AudioTestObjAddMetadataStr(Obj, "beacon_pre_bytes=%RU32\n", cbBeacon);
    886890        AudioTestObjAddMetadataStr(Obj, "beacon_post_bytes=%RU32\n", cbBeacon);
    887         AudioTestObjAddMetadataStr(Obj, "stream_to_record_bytes=%RU32\n", cbTestToRec);
     891        AudioTestObjAddMetadataStr(Obj, "stream_to_record_bytes=%RU32\n", cbToReadTotal);
    888892        AudioTestObjAddMetadataStr(Obj, "stream_buffer_size_ms=%RU32\n", pIoOpts->cMsBufferSize);
    889893        AudioTestObjAddMetadataStr(Obj, "stream_prebuf_size_ms=%RU32\n", pIoOpts->cMsPreBuffer);
     
    892896        AudioTestObjAddMetadataStr(Obj, "device_scheduling_hint_ms=%RU32\n", pIoOpts->cMsSchedulingHint);
    893897
    894         uint8_t         abSamples[16384];
    895         uint32_t const  cbSamplesAligned  = PDMAudioPropsFloorBytesToFrame(pMix->pProps, sizeof(abSamples));
     898        PRTCIRCBUF pCircBuf;
     899        rc = RTCircBufCreate(&pCircBuf, _64K);
     900        AssertRCReturn(rc, rc);
    896901
    897902        uint64_t const  nsStarted         = RTTimeNanoTS();
     
    904909        while (!g_fTerminate)
    905910        {
    906             uint64_t const nsNow = RTTimeNanoTS();
     911            uint64_t const nsNow  = RTTimeNanoTS();
     912
     913            void  *pvBlock;
     914            size_t cbBlock;
    907915
    908916            /*
    909              * Anything we can read?
     917             * Anything we can capture from the stream?
    910918             */
    911919            uint32_t const cbCanRead = AudioTestMixStreamGetReadable(pMix);
    912920            if (cbCanRead)
    913921            {
    914                 if (g_uVerbosity >= 3)
    915                     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Stream is readable with %RU64ms (%RU32 bytes)\n",
    916                                  idxTest, PDMAudioPropsBytesToMilli(pMix->pProps, cbCanRead), cbCanRead);
    917 
    918                 uint32_t const cbToRead   = RT_MIN(cbCanRead, cbSamplesAligned);
    919                 uint32_t       cbRecorded = 0;
    920                 rc = AudioTestMixStreamCapture(pMix, abSamples, cbToRead, &cbRecorded);
    921                 if (RT_SUCCESS(rc))
     922                RTCircBufAcquireWriteBlock(pCircBuf, cbCanRead, &pvBlock, &cbBlock);
     923
     924                uint32_t cbCaptured = 0;
     925                if (cbBlock)
    922926                {
    923                     /* Flag indicating whether the whole block we're going to play is silence or not. */
    924                     bool const fIsAllSilence = PDMAudioPropsIsBufferSilence(&pStream->pStream->Cfg.Props, abSamples, cbRecorded);
    925 
    926                     cbRecTotal += cbRecorded; /* Do a bit of accounting. */
    927 
    928                     switch (enmState)
    929                     {
    930                         case AUDIOTESTSTATE_PRE:
    931                             RT_FALL_THROUGH();
    932                         case AUDIOTESTSTATE_POST:
    933                         {
    934                             bool fGoToNextStage = false;
    935 
    936                             if (    AudioTestBeaconGetSize(&Beacon)
    937                                 && !AudioTestBeaconIsComplete(&Beacon))
    938                             {
    939                                 bool const fStarted = AudioTestBeaconGetRemaining(&Beacon) == AudioTestBeaconGetSize(&Beacon);
    940 
    941                                 size_t uOff;
    942                                 rc = AudioTestBeaconAddConsecutive(&Beacon, abSamples, cbRecorded, &uOff);
    943                                 if (RT_SUCCESS(rc))
    944                                 {
    945                                     /*
    946                                      * When being in the AUDIOTESTSTATE_PRE state, we might get more audio data
    947                                      * than we need for the pre-beacon to complete. In other words, that "more data"
    948                                      * needs to be counted to the actual recorded test tone data then.
    949                                      */
    950                                     if (enmState == AUDIOTESTSTATE_PRE)
    951                                         cbTestRec += cbRecorded - (uint32_t)uOff;
    952                                 }
    953 
    954                                 if (   fStarted
    955                                     && g_uVerbosity >= 3)
    956                                     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
    957                                                  "Test #%RU32: Detection of %s beacon started (%RU32ms recorded so far)\n",
    958                                                  idxTest, AudioTestBeaconTypeGetName(Beacon.enmType),
    959                                                  PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbRecTotal));
    960 
    961                                 if (AudioTestBeaconIsComplete(&Beacon))
    962                                 {
    963                                     if (g_uVerbosity >= 2)
    964                                         RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Detected %s beacon\n",
    965                                                      idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
    966                                     fGoToNextStage = true;
    967                                 }
    968                             }
    969                             else
    970                                 fGoToNextStage = true;
    971 
    972                             if (fGoToNextStage)
    973                             {
    974                                 if (enmState == AUDIOTESTSTATE_PRE)
    975                                     enmState = AUDIOTESTSTATE_RUN;
    976                                 else if (enmState == AUDIOTESTSTATE_POST)
    977                                     enmState = AUDIOTESTSTATE_DONE;
    978                             }
    979                             break;
    980                         }
    981 
    982                         case AUDIOTESTSTATE_RUN:
    983                         {
    984                             /* Whether we count all silence as recorded data or not.
    985                              * Currently we don't, as otherwise consequtively played tones will be cut off in the end. */
    986                             if (!fIsAllSilence)
    987                             {
    988                                 uint32_t const cbToAddMax = cbTestToRec - cbTestRec;
    989 
    990                                 /* Don't read more than we're told to.
    991                                  * After the actual test tone data there might come a post beacon which also
    992                                  * needs to be handled in the AUDIOTESTSTATE_POST state then. */
    993                                 if (cbRecorded > cbToAddMax)
    994                                     cbRecorded = cbToAddMax;
    995 
    996                                 cbTestRec += cbRecorded;
    997                             }
    998 
    999                             if (cbTestToRec - cbTestRec == 0) /* Done recording the test tone? */
    1000                             {
    1001                                 enmState = AUDIOTESTSTATE_POST;
    1002 
    1003                                 if (g_uVerbosity >= 2)
    1004                                     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recording tone data done\n", idxTest);
    1005 
    1006                                 if (AudioTestBeaconGetSize(&Beacon))
    1007                                 {
    1008                                     /* Re-use the beacon object, but this time it's the post beacon. */
    1009                                     AudioTestBeaconInit(&Beacon, (uint8_t)pParms->Hdr.idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_POST,
    1010                                                         &pStream->Cfg.Props);
    1011                                     if (g_uVerbosity >= 2)
    1012                                         RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
    1013                                                      "Test #%RU32: Waiting for %s beacon ...\n",
    1014                                                      idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
    1015                                 }
    1016                             }
    1017                             break;
    1018                         }
    1019 
    1020                         case AUDIOTESTSTATE_DONE:
    1021                         {
    1022                             /* Nothing to do here. */
    1023                             break;
    1024                         }
    1025 
    1026                         default:
    1027                             AssertFailed();
    1028                             break;
    1029                     }
     927                    rc = AudioTestMixStreamCapture(pMix, pvBlock, cbBlock, &cbCaptured);
     928                    if (RT_FAILURE(rc))
     929                       RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Reading from stream failed with %Rrc\n", idxTest, rc);
     930
     931                    if (g_uVerbosity >= 2)
     932                        RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Stream is readable with %RU64ms (%RU32 bytes), captured %RU64ms (%RU32 bytes)\n",
     933                                     idxTest,
     934                                     PDMAudioPropsBytesToMilli(pMix->pProps, cbCanRead), cbCanRead,
     935                                     PDMAudioPropsBytesToMilli(pMix->pProps, cbCaptured), cbCaptured);
     936
     937                    if (RT_FAILURE(rc))
     938                        RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Capturing failed with %Rrc\n",
     939                                     idxTest, rc);
    1030940                }
    1031941
    1032                 if (cbRecorded)
    1033                 {
    1034                     /* Always write (record) everything, no matter if the current audio contains complete silence or not.
    1035                      * Might be also become handy later if we want to have a look at start/stop timings and so on. */
    1036                     rc = AudioTestObjWrite(Obj, abSamples, cbRecorded);
    1037                     AssertRCBreak(rc);
    1038                 }
    1039 
    1040                 if (enmState == AUDIOTESTSTATE_DONE) /* Bail out when in state "done". */
    1041                     break;
     942                RTCircBufReleaseWriteBlock(pCircBuf, cbCaptured);
    1042943            }
    1043944            else if (AudioTestMixStreamIsOkay(pMix))
     
    1057958            }
    1058959
     960            /*
     961             * Process our buffer.
     962             */
     963            size_t cbBlockToAcq;
     964            if (pTstEnv->fSelftest) /* For self-test mode we want to have a bit more randomness. */
     965            {
     966                size_t const u = RTCircBufUsed(pCircBuf);
     967                size_t const r = RTRandU32Ex(1, RTCircBufSize(pCircBuf));
     968                cbBlockToAcq   = PDMAudioPropsFloorBytesToFrame(pMix->pProps, RT_MIN(u, r));
     969            }
     970            else
     971                cbBlockToAcq = PDMAudioPropsFloorBytesToFrame(pMix->pProps, RTCircBufUsed(pCircBuf));
     972
     973            RTCircBufAcquireReadBlock(pCircBuf, cbBlockToAcq, &pvBlock, &cbBlock);
     974            if (!cbBlock)
     975                continue;
     976
     977            /* Flag indicating whether the whole block we've captured is silence or not. */
     978            bool const fIsAllSilence = PDMAudioPropsIsBufferSilence(&pStream->pStream->Cfg.Props, pvBlock, cbBlock);
     979            uint32_t   cbRead = 0;
     980
     981            switch (enmState)
     982            {
     983                case AUDIOTESTSTATE_PRE:
     984                    RT_FALL_THROUGH();
     985                case AUDIOTESTSTATE_POST:
     986                {
     987                    bool fGoToNextStage = false;
     988
     989                    if (    AudioTestBeaconGetSize(&Beacon)
     990                        && !AudioTestBeaconIsComplete(&Beacon))
     991                    {
     992                        bool const fStarted = AudioTestBeaconGetRemaining(&Beacon) == AudioTestBeaconGetSize(&Beacon);
     993
     994                        size_t uOff;
     995                        int rc2 = AudioTestBeaconAddConsecutive(&Beacon, (uint8_t *)pvBlock, cbBlock, &uOff);
     996                        if (   RT_SUCCESS(rc2)
     997                            && uOff)
     998                            cbRead = uOff;
     999
     1000                        if (   fStarted
     1001                            && g_uVerbosity >= 2)
     1002                        {
     1003                            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
     1004                                         "Test #%RU32: Detection of %s beacon started (%RU32ms recorded so far)\n",
     1005                                         idxTest, AudioTestBeaconTypeGetName(Beacon.enmType),
     1006                                         PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbReadTotal));
     1007                        }
     1008
     1009                        if (AudioTestBeaconIsComplete(&Beacon))
     1010                        {
     1011                            if (g_uVerbosity >= 2)
     1012                                RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Detection of %s beacon complete\n",
     1013                                             idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
     1014                            fGoToNextStage = true;
     1015                        }
     1016                    }
     1017                    else
     1018                        fGoToNextStage = true;
     1019
     1020                    if (fGoToNextStage)
     1021                    {
     1022                        if (enmState == AUDIOTESTSTATE_PRE)
     1023                            enmState = AUDIOTESTSTATE_RUN;
     1024                        else if (enmState == AUDIOTESTSTATE_POST)
     1025                            enmState = AUDIOTESTSTATE_DONE;
     1026                    }
     1027                    break;
     1028                }
     1029
     1030                case AUDIOTESTSTATE_RUN:
     1031                {
     1032                    /* Whether we count all silence as recorded data or not.
     1033                     * Currently we don't, as otherwise consequtively played tones will be cut off in the end. */
     1034                    bool const fRecord = !fIsAllSilence;
     1035                    if (fRecord)
     1036                    {
     1037                        /* Don't read more than we're told to.
     1038                         * After the actual test tone data there might come a post beacon which also
     1039                         * needs to be handled in the AUDIOTESTSTATE_POST state then. */
     1040                        if (cbReadTotal + cbBlock > cbToReadTotal)
     1041                        {
     1042                            AssertBreakStmt(cbToReadTotal >= cbReadTotal, rc = VERR_INTERNAL_ERROR);
     1043                            cbRead = cbToReadTotal - cbReadTotal;
     1044                        }
     1045                        else
     1046                            cbRead = cbBlock;
     1047
     1048                        cbReadTotal += cbRead;
     1049                        AssertBreakStmt(cbReadTotal <= cbToReadTotal, rc = VERR_INTERNAL_ERROR);
     1050                    }
     1051
     1052                    /* Done recording the test tone? */
     1053                    if (cbReadTotal + cbRead == cbToReadTotal)
     1054                    {
     1055                        enmState = AUDIOTESTSTATE_POST;
     1056
     1057                        if (g_uVerbosity >= 2)
     1058                            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recording tone data done\n", idxTest);
     1059
     1060                        if (AudioTestBeaconGetSize(&Beacon))
     1061                        {
     1062                            /* Re-use the beacon object, but this time it's the post beacon. */
     1063                            AudioTestBeaconInit(&Beacon, (uint8_t)pParms->Hdr.idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_POST,
     1064                                                &pStream->Cfg.Props);
     1065                            if (g_uVerbosity >= 2)
     1066                                RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
     1067                                             "Test #%RU32: Waiting for %s beacon ...\n",
     1068                                             idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
     1069                        }
     1070                    }
     1071                    break;
     1072                }
     1073
     1074                case AUDIOTESTSTATE_DONE:
     1075                {
     1076                    /* Nothing to do here. */
     1077                    break;
     1078                }
     1079
     1080                default:
     1081                    AssertFailed();
     1082                    break;
     1083            }
     1084
     1085            if (cbRead)
     1086            {
     1087                if (g_uVerbosity >= 3)
     1088                    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Read data (%RU32 bytes):\n"
     1089                                                            "%.*Rhxd\n",
     1090                                 idxTest, cbRead, cbRead, pvBlock);
     1091
     1092                /* Always write (record) everything, no matter if the current audio contains complete silence or not.
     1093                 * Might be also become handy later if we want to have a look at start/stop timings and so on. */
     1094                rc = AudioTestObjWrite(Obj, pvBlock, cbRead);
     1095                AssertRCBreak(rc);
     1096            }
     1097
     1098            if (g_uVerbosity >= 2)
     1099                RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Processed %RU64ms (%RU32 bytes)\n",
     1100                             idxTest, PDMAudioPropsBytesToMilli(pMix->pProps, cbRead), cbRead);
     1101
     1102            RTCircBufReleaseReadBlock(pCircBuf, cbRead);
     1103
     1104            if (enmState == AUDIOTESTSTATE_DONE) /* Bail out when in state "done". */
     1105                break;
     1106
    10591107            /* Fail-safe in case something screwed up while playing back. */
    10601108            uint64_t const cNsElapsed = nsNow - nsStarted;
     
    10681116            if (RT_FAILURE(rc))
    10691117                break;
    1070         }
     1118        } /* while */
    10711119
    10721120        if (g_uVerbosity >= 2)
    1073             RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recorded %RU32 bytes total\n", idxTest, cbRecTotal);
    1074         if (cbTestRec != cbTestToRec)
    1075         {
    1076             RTTestFailed(g_hTest, "Test #%RU32: Recording ended unexpectedly (%RU32/%RU32 recorded)\n",
    1077                          idxTest, cbTestRec, cbTestToRec);
    1078             rc = VERR_WRONG_ORDER; /** @todo Find a better rc. */
     1121            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recorded %RU32 bytes total\n", idxTest, cbReadTotal);
     1122        if (cbReadTotal != cbToReadTotal)
     1123        {
     1124            RTTestFailed(g_hTest, "Test #%RU32: Recording ended unexpectedly (%RU32 read, expected %RU32)\n",
     1125                         idxTest, cbReadTotal, cbToReadTotal);
     1126            int rc2 = cbReadTotal > cbToReadTotal ? VERR_BUFFER_OVERFLOW : VERR_BUFFER_UNDERFLOW;
     1127            if (RT_SUCCESS(rc))
     1128                rc = rc2;
    10791129        }
    10801130
     
    10851135        if (RT_SUCCESS(rc))
    10861136            rc = rc2;
     1137
     1138        RTCircBufDestroy(pCircBuf);
    10871139    }
    10881140
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