VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/audio/vkat.cpp@ 88929

Last change on this file since 88929 was 88929, checked in by vboxsync, 4 years ago

Audio/VaKit: Fixed warning. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.1 KB
Line 
1/* $Id: vkat.cpp 88929 2021-05-07 15:01:34Z vboxsync $ */
2/** @file
3 * Validation Kit Audio Test (VKAT) utility for testing and validating the audio stack.
4 */
5
6/*
7 * Copyright (C) 2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/errcore.h>
32#include <iprt/initterm.h>
33#include <iprt/getopt.h>
34#include <iprt/path.h>
35#include <iprt/message.h>
36#include <iprt/process.h>
37#include <iprt/stream.h>
38#include <iprt/string.h>
39#include <iprt/test.h>
40
41#include <VBox/vmm/pdmaudioinline.h>
42
43#include "../../../Devices/Audio/AudioHlp.h"
44#include "../../../Devices/Audio/AudioTest.h"
45#include "../../../Devices/Audio/VBoxDDVKAT.h"
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56
57
58/**
59 * Audio test request data.
60 */
61typedef struct AUDIOTESTPARMS
62{
63 /** Specifies the test to run. */
64 uint32_t idxTest;
65 /** How many iterations the test should be executed. */
66 uint32_t cIterations;
67 /** Name or path of audio device to use, depending on the OS.
68 * If NULL, the default device for this specific test (input / output) will be used. */
69 char *pszDevice;
70 /** Absolute path where to store the test audio data.
71 * If NULL, no test audio data will be written. */
72 char *pszPathOutAbs;
73 /** How much to delay (wait, in ms) the test being executed. */
74 RTMSINTERVAL msDelay;
75 /** The test type. */
76 PDMAUDIODIR enmDir;
77 union
78 {
79 AUDIOTESTTONEPARMS ToneParms;
80 };
81} AUDIOTESTPARMS;
82/** Pointer to a test parameter structure. */
83typedef AUDIOTESTPARMS *PAUDIOTESTPARMS;
84
85struct AUDIOTESTENV;
86struct AUDIOTESTDESC;
87
88/**
89 * Callback to set up the test parameters for a specific test.
90 *
91 * @returns IPRT status code.
92 * @retval VINF_SUCCESS if setting the parameters up succeeded. Any other error code
93 * otherwise indicating the kind of error.
94 * @param pszTest Test name.
95 * @param pTstParmsAcq The audio test parameters to set up.
96 */
97typedef DECLCALLBACKTYPE(int, FNAUDIOTESTSETUP,(AUDIOTESTENV *pTstEnv, AUDIOTESTDESC *pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx));
98/** Pointer to an audio test setup callback. */
99typedef FNAUDIOTESTSETUP *PFNAUDIOTESTSETUP;
100
101typedef DECLCALLBACKTYPE(int, FNAUDIOTESTEXEC,(AUDIOTESTENV *pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms));
102/** Pointer to an audio test exec callback. */
103typedef FNAUDIOTESTEXEC *PFNAUDIOTESTEXEC;
104
105typedef DECLCALLBACKTYPE(int, FNAUDIOTESTDESTROY,(AUDIOTESTENV *pTstEnv, void *pvCtx));
106/** Pointer to an audio test destroy callback. */
107typedef FNAUDIOTESTDESTROY *PFNAUDIOTESTDESTROY;
108
109/**
110 * Audio test environment parameters.
111 * Not necessarily bound to a specific test (can be reused).
112 */
113typedef struct AUDIOTESTENV
114{
115 PPDMIHOSTAUDIO pDrvAudio;
116} AUDIOTESTENV;
117/** Pointer a audio test environment. */
118typedef AUDIOTESTENV *PAUDIOTESTENV;
119
120/**
121 * Audio test descriptor.
122 */
123typedef struct AUDIOTESTDESC
124{
125 /** (Sort of) Descriptive test name. */
126 const char *pszName;
127 /** Flag whether the test is excluded. */
128 bool fExcluded;
129 /** The setup callback. */
130 PFNAUDIOTESTSETUP pfnSetup;
131 /** The exec callback. */
132 PFNAUDIOTESTEXEC pfnExec;
133 /** The destruction callback. */
134 PFNAUDIOTESTDESTROY pfnDestroy;
135} AUDIOTESTDESC;
136/** Pointer a audio test descriptor. */
137typedef AUDIOTESTDESC *PAUDIOTESTDESC;
138
139
140/*********************************************************************************************************************************
141* Global Variables *
142*********************************************************************************************************************************/
143
144enum
145{
146 VKAT_TEST_OPT_COUNT = 900,
147 VKAT_TEST_OPT_DEV,
148 VKAT_TEST_OPT_OUTDIR,
149 VKAT_TEST_OPT_PAUSE,
150 VKAT_TEST_OPT_HZ,
151 VKAT_TEST_OPT_BIT,
152 VKAT_TEST_OPT_CHAN,
153 VKAT_TEST_OPT_SIGNED,
154 VKAT_TEST_OPT_VOL
155};
156
157#if 0
158static const RTGETOPTDEF g_aCmdCommonOptions[] =
159{
160 { "--help", 'h', RTGETOPT_REQ_NOTHING }
161};
162#endif
163
164/** Command line parameters for test mode. */
165static const RTGETOPTDEF g_aCmdTestOptions[] =
166{
167 { "--backend", 'b', RTGETOPT_REQ_STRING },
168 { "--exclude", 'e', RTGETOPT_REQ_UINT32 },
169 { "--exclude-all", 'a', RTGETOPT_REQ_NOTHING },
170 { "--include", 'i', RTGETOPT_REQ_UINT32 },
171 { "--outdir", VKAT_TEST_OPT_OUTDIR, RTGETOPT_REQ_STRING },
172 { "--count", VKAT_TEST_OPT_COUNT, RTGETOPT_REQ_UINT32 },
173 { "--device", VKAT_TEST_OPT_DEV, RTGETOPT_REQ_STRING },
174 { "--pause", VKAT_TEST_OPT_PAUSE, RTGETOPT_REQ_UINT32 },
175 { "--pcm-bit", VKAT_TEST_OPT_BIT, RTGETOPT_REQ_UINT8 },
176 { "--pcm-chan", VKAT_TEST_OPT_CHAN, RTGETOPT_REQ_UINT8 },
177 { "--pcm-hz", VKAT_TEST_OPT_HZ, RTGETOPT_REQ_UINT16 },
178 { "--pcm-signed", VKAT_TEST_OPT_SIGNED, RTGETOPT_REQ_BOOL },
179 { "--volume", VKAT_TEST_OPT_VOL, RTGETOPT_REQ_UINT8 }
180
181};
182
183/** The test handle. */
184static RTTEST g_hTest;
185/** The driver instance data. */
186PDMDRVINS g_DrvIns;
187
188
189/*********************************************************************************************************************************
190* Prototypes *
191*********************************************************************************************************************************/
192static int audioTestCombineParms(PAUDIOTESTPARMS pBaseParms, PAUDIOTESTPARMS pOverrideParms);
193
194
195/*********************************************************************************************************************************
196* Test callbacks *
197*********************************************************************************************************************************/
198
199/**
200 * Setup callback for playing an output tone.
201 *
202 * @copydoc FNAUDIOTESTSETUP
203 */
204static DECLCALLBACK(int) audioTestPlayToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)
205{
206 RT_NOREF(pTstEnv, pTstDesc, pTstParmsAcq, ppvCtx);
207
208// PDMAudioPropsInit(&Props, 16 /* bit */ / 8, true /* fSigned */, 2 /* Channels */, 44100 /* Hz */);
209
210 //AudioTestToneParamsInitRandom(&pTestParms->ToneParms, &pTestParms->ToneParms.Props);
211
212 return VINF_SUCCESS;
213}
214
215static DECLCALLBACK(int) audioTestPlayToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)
216{
217 RT_NOREF(pTstEnv, pvCtx, pTstParms);
218
219 return VINF_SUCCESS;
220}
221
222static DECLCALLBACK(int) audioTestPlayToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)
223{
224 RT_NOREF(pTstEnv, pvCtx);
225
226 return VINF_SUCCESS;
227}
228
229
230/*********************************************************************************************************************************
231* Implementation *
232*********************************************************************************************************************************/
233
234static void audioTestParmsInit(PAUDIOTESTPARMS pTestParms)
235{
236 RT_BZERO(pTestParms, sizeof(AUDIOTESTPARMS));
237 return;
238}
239
240static void audioTestParmsDestroy(PAUDIOTESTPARMS pTestParms)
241{
242 if (!pTestParms)
243 return;
244
245 RTStrFree(pTestParms->pszDevice);
246 pTestParms->pszDevice = NULL;
247
248 RTStrFree(pTestParms->pszPathOutAbs);
249 pTestParms->pszPathOutAbs = NULL;
250
251 return;
252}
253
254static AUDIOTESTDESC g_aTests[] =
255{
256 /* pszTest fExcluded pfnSetup */
257 { "PlayTone", false, audioTestPlayToneSetup, audioTestPlayToneExec, audioTestPlayToneDestroy }
258};
259
260/**
261 * Shows tool usage text.
262 */
263static void audioTestUsage(PRTSTREAM pStrm)
264{
265 char szExec[RTPATH_MAX];
266 RTStrmPrintf(pStrm, "usage: %s [options]\n",
267 RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
268 RTStrmPrintf(pStrm, "\n");
269 RTStrmPrintf(pStrm, "options: \n");
270
271 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdTestOptions); i++)
272 {
273 const char *pszHelp;
274 switch (g_aCmdTestOptions[i].iShort)
275 {
276 case 'h':
277 pszHelp = "Displays this help and exit";
278 break;
279 case 'd':
280 pszHelp = "Use the specified audio device";
281 break;
282 case 'e':
283 pszHelp = "Exclude the given test id from the list";
284 break;
285 case 'a':
286 pszHelp = "Exclude all tests from the list (useful to enable single tests later with --include)";
287 break;
288 case 'i':
289 pszHelp = "Include the given test id in the list";
290 break;
291 default:
292 pszHelp = "Option undocumented";
293 break;
294 }
295 char szOpt[256];
296 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdTestOptions[i].pszLong, g_aCmdTestOptions[i].iShort);
297 RTStrmPrintf(pStrm, " %-30s%s\n", szOpt, pszHelp);
298 }
299
300 /** @todo Add all other options. */
301}
302
303static int audioTestDrvConstruct(const PDMDRVREG *pDrvReg, PPDMDRVINS pDrvIns, PPDMIHOSTAUDIO *ppDrvAudio)
304{
305 AssertReturn(pDrvReg->cbInstance, VERR_INVALID_PARAMETER); /** @todo Very crude; improve. */
306
307 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Initializing backend '%s' ...\n", pDrvReg->szName);
308
309 pDrvIns->pvInstanceData = RTMemAllocZ(pDrvReg->cbInstance);
310 AssertPtrReturn(pDrvIns->pvInstanceData, VERR_NO_MEMORY);
311
312 int rc = pDrvReg->pfnConstruct(pDrvIns, NULL /* PCFGMNODE */, 0 /* fFlags */);
313 if (RT_SUCCESS(rc))
314 {
315 PPDMIHOSTAUDIO pDrvAudio = (PPDMIHOSTAUDIO)pDrvIns->IBase.pfnQueryInterface(&pDrvIns->IBase, PDMIHOSTAUDIO_IID);
316
317 pDrvAudio->pfnGetStatus(pDrvAudio, PDMAUDIODIR_OUT);
318
319 *ppDrvAudio = pDrvAudio;
320 }
321
322 return rc;
323}
324
325static int audioTestDrvDestruct(const PDMDRVREG *pDrvReg, PPDMDRVINS pDrvIns)
326{
327 if (!pDrvIns)
328 return VINF_SUCCESS;
329
330 if (pDrvReg->pfnDestruct)
331 pDrvReg->pfnDestruct(pDrvIns);
332
333 if (pDrvIns->pvInstanceData)
334 {
335 RTMemFree(pDrvIns->pvInstanceData);
336 pDrvIns->pvInstanceData = NULL;
337 }
338
339 return VINF_SUCCESS;
340}
341
342/**
343 * Searches for the default audio test device and return the device path.
344 *
345 * @returns Path to the device audio device or NULL if none was found.
346 */
347static char *audioTestDeviceFindDefault(void)
348{
349 /** @todo Implement finding default device. */
350 return NULL;
351}
352
353static int audioTestDeviceOpen(const char *pszDevice)
354{
355 int rc = VINF_SUCCESS;
356
357 RTTestSubF(g_hTest, "Opening audio device '%s' ...", pszDevice ? "<Default>>" : pszDevice);
358
359 if (!pszDevice)
360 audioTestDeviceFindDefault();
361
362 /** @todo Detect + open device here. */
363
364 RTTestSubDone(g_hTest);
365
366 return rc;
367}
368
369static int audioTestCombineParms(PAUDIOTESTPARMS pBaseParms, PAUDIOTESTPARMS pOverrideParms)
370{
371 RT_NOREF(pBaseParms, pOverrideParms);
372 return 0;
373}
374
375static int audioTestOne(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc,
376 unsigned uSeq, PAUDIOTESTPARMS pOverrideParms)
377{
378 int rc;
379
380 AUDIOTESTPARMS TstParms;
381 audioTestParmsInit(&TstParms);
382
383 RTTestSub(g_hTest, pTstDesc->pszName);
384
385 if (pTstDesc->fExcluded)
386 {
387 RTTestSkipped(g_hTest, "Excluded from list");
388 return VINF_SUCCESS;
389 }
390
391 void *pvCtx = NULL;
392
393 if (pTstDesc->pfnSetup)
394 {
395 rc = pTstDesc->pfnSetup(pTstEnv, pTstDesc, &TstParms, &pvCtx);
396 if (RT_FAILURE(rc))
397 return rc;
398 }
399
400 audioTestCombineParms(&TstParms, pOverrideParms);
401
402 /* Open the device on the first test being run. */
403 if ( uSeq == 0
404 && TstParms.pszDevice
405 && strlen(TstParms.pszDevice))
406 {
407 rc = audioTestDeviceOpen(TstParms.pszDevice);
408 if (RT_FAILURE(rc))
409 RTTestFailed(g_hTest, "Unable to find audio device '%s'", TstParms.pszDevice);
410 }
411
412 AssertPtr(pTstDesc->pfnExec);
413 rc = pTstDesc->pfnExec(pTstEnv, pvCtx, &TstParms);
414
415 RTTestSubDone(g_hTest);
416
417 if (pTstDesc->pfnDestroy)
418 {
419 int rc2 = pTstDesc->pfnDestroy(pTstEnv, pvCtx);
420 if (RT_SUCCESS(rc))
421 rc = rc2;
422 }
423
424 audioTestParmsDestroy(&TstParms);
425
426 return rc;
427}
428
429static int audioTestWorker(PAUDIOTESTENV pTstEnv, PAUDIOTESTPARMS pOverrideParms)
430{
431 int rc = VINF_SUCCESS;
432
433 unsigned uSeq = 0;
434 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
435 {
436 int rc2 = audioTestOne(pTstEnv, &g_aTests[i], uSeq, pOverrideParms);
437 if (RT_SUCCESS(rc))
438 rc = rc2;
439
440 if (!g_aTests[i].fExcluded)
441 uSeq++;
442 }
443
444 return rc;
445}
446
447int mainTest(int argc, char **argv)
448{
449 int rc;
450
451 AUDIOTESTPARMS TstCust;
452 audioTestParmsInit(&TstCust);
453
454 RT_ZERO(g_DrvIns);
455 const PDMDRVREG *pDrvReg = NULL;
456
457 RTGETOPTUNION ValueUnion;
458 RTGETOPTSTATE GetState;
459 RTGetOptInit(&GetState, argc, argv, g_aCmdTestOptions, RT_ELEMENTS(g_aCmdTestOptions), 0, 0 /* fFlags */);
460 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
461 {
462 switch (rc)
463 {
464 case 'h':
465 {
466 audioTestUsage(g_pStdOut);
467 return RTEXITCODE_SUCCESS;
468 }
469
470 case 'e':
471 {
472 if (ValueUnion.u32 < RT_ELEMENTS(g_aTests))
473 g_aTests[ValueUnion.u32].fExcluded = true;
474 else
475 {
476 RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid test number passed to --exclude\n");
477 RTTestErrorInc(g_hTest);
478 return RTGetOptPrintError(VERR_INVALID_PARAMETER, &ValueUnion);
479 }
480 break;
481 }
482
483 case 'a':
484 {
485 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
486 g_aTests[i].fExcluded = true;
487 break;
488 }
489
490 case 'b':
491 {
492#ifdef VBOX_WITH_AUDIO_PULSE
493 if ( !RTStrICmp(ValueUnion.psz, "pulseaudio")
494 || !RTStrICmp(ValueUnion.psz, "pa"))
495 pDrvReg = &g_DrvVKATPulseAudio;
496#endif
497#ifdef VBOX_WITH_AUDIO_ALSA
498 if ( !RTStrICmp(ValueUnion.psz, "alsa"))
499 pDrvReg = &g_DrvVKATAlsa;
500#endif
501#ifdef VBOX_WITH_AUDIO_OSS
502 if ( !RTStrICmp(ValueUnion.psz, "oss"))
503 pDrvReg = &g_DrvVKATOss;
504#endif
505 /** @todo Add more backends here. */
506
507 if (pDrvReg == NULL)
508 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid / unsupported backend '%s' specified\n", ValueUnion.psz);
509 break;
510 }
511
512 case 'i':
513 {
514 if (ValueUnion.u32 < RT_ELEMENTS(g_aTests))
515 g_aTests[ValueUnion.u32].fExcluded = false;
516 else
517 {
518 RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid test number passed to --include\n");
519 RTTestErrorInc(g_hTest);
520 return RTGetOptPrintError(VERR_INVALID_PARAMETER, &ValueUnion);
521 }
522 break;
523 }
524
525 case VKAT_TEST_OPT_COUNT:
526 {
527 break;
528 }
529
530 case VKAT_TEST_OPT_DEV:
531 {
532 TstCust.pszDevice = RTStrDup(ValueUnion.psz);
533 break;
534 }
535
536 case VKAT_TEST_OPT_PAUSE:
537 {
538 break;
539 }
540
541 case VKAT_TEST_OPT_OUTDIR:
542 {
543 TstCust.pszPathOutAbs = RTStrDup(ValueUnion.psz);
544 break;
545 }
546
547 case VKAT_TEST_OPT_BIT:
548 {
549 TstCust.ToneParms.Props.cbSampleX = ValueUnion.u8 / 8 /* bit */;
550 break;
551 }
552
553 case VKAT_TEST_OPT_CHAN:
554 {
555 TstCust.ToneParms.Props.cChannelsX = ValueUnion.u8;
556 break;
557 }
558
559 case VKAT_TEST_OPT_HZ:
560 {
561 TstCust.ToneParms.Props.uHz = ValueUnion.u32;
562 break;
563 }
564
565 case VKAT_TEST_OPT_SIGNED:
566 {
567 TstCust.ToneParms.Props.fSigned = ValueUnion.f;
568 break;
569 }
570
571 case VKAT_TEST_OPT_VOL:
572 {
573 TstCust.ToneParms.uVolumePercent = ValueUnion.u8;
574 break;
575 }
576
577 default:
578 break;
579 }
580 }
581
582 /*
583 * Start testing.
584 */
585 RTTestBanner(g_hTest);
586
587 /* If no backend is specified, go with the ALSA one by default. */
588 if (pDrvReg == NULL)
589 pDrvReg = &g_DrvVKATAlsa;
590
591 PPDMIHOSTAUDIO pDrvAudio;
592 rc = audioTestDrvConstruct(pDrvReg, &g_DrvIns, &pDrvAudio);
593 if (RT_SUCCESS(rc))
594 {
595 /* For now all tests have the same test environment. */
596 AUDIOTESTENV TestEnv;
597 TestEnv.pDrvAudio = pDrvAudio;
598
599 audioTestWorker(&TestEnv, &TstCust);
600
601 audioTestDrvDestruct(pDrvReg, &g_DrvIns);
602 }
603
604 audioTestParmsDestroy(&TstCust);
605
606 /*
607 * Print summary and exit.
608 */
609 return RTTestSummaryAndDestroy(g_hTest);
610}
611
612int mainVerify(int argc, char **argv)
613{
614 RT_NOREF(argc, argv);
615 return 0;
616}
617
618int main(int argc, char **argv)
619{
620 /*
621 * Init IPRT and globals.
622 */
623 int rc = RTTestInitAndCreate("AudioTest", &g_hTest);
624 if (rc)
625 return rc;
626
627 if (argc < 2)
628 {
629 audioTestUsage(g_pStdOut);
630 return RTEXITCODE_SYNTAX;
631 }
632
633 const char *pszMode = argv[1];
634
635 argc -= 2;
636 argv += 2;
637
638 if (!RTStrICmp(pszMode, "test"))
639 {
640 return mainTest(argc, argv);
641 }
642 else if (!RTStrICmp(pszMode, "verify"))
643 {
644 return mainVerify(argc, argv);
645 }
646
647 RTStrmPrintf(g_pStdOut, "Must specify a mode first, either 'test' or 'verify'\n\n");
648
649 audioTestUsage(g_pStdOut);
650 return RTEXITCODE_SYNTAX;
651}
652
Note: See TracBrowser for help on using the repository browser.

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