VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioTest.cpp@ 89027

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

Audio/ValKit: More work on test tone execution. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/* $Id: AudioTest.cpp 89014 2021-05-12 14:33:19Z vboxsync $ */
2/** @file
3 * Audio testing routines.
4 * Common code which is being used by the ValidationKit and the debug / ValdikationKit audio driver(s).
5 */
6
7/*
8 * Copyright (C) 2021 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23
24#include <package-generated.h>
25#include "product-generated.h"
26
27#include <iprt/buildconfig.h>
28#include <iprt/dir.h>
29#include <iprt/file.h>
30#include <iprt/inifile.h>
31#include <iprt/list.h>
32#include <iprt/rand.h>
33#include <iprt/system.h>
34#include <iprt/uuid.h>
35#include <iprt/vfs.h>
36
37#define _USE_MATH_DEFINES
38#include <math.h> /* sin, M_PI */
39
40#include <VBox/version.h>
41#include <VBox/vmm/pdmaudioifs.h>
42#include <VBox/vmm/pdmaudioinline.h>
43
44#include "AudioTest.h"
45
46
47/*********************************************************************************************************************************
48* Defines *
49*********************************************************************************************************************************/
50/** The test manifest file. */
51#define AUDIOTEST_MANIFEST_FILE_STR "vkat_manifest.ini"
52#define AUDIOTEST_MANIFEST_VER 1
53
54#define AUDIOTEST_INI_SEC_HDR_STR "header"
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60
61
62/*********************************************************************************************************************************
63* Global Variables *
64*********************************************************************************************************************************/
65/** Well-known frequency selection test tones. */
66static const double s_aAudioTestToneFreqsHz[] =
67{
68 349.2282 /*F4*/,
69 440.0000 /*A4*/,
70 523.2511 /*C5*/,
71 698.4565 /*F5*/,
72 880.0000 /*A5*/,
73 1046.502 /*C6*/,
74 1174.659 /*D6*/,
75 1396.913 /*F6*/,
76 1760.0000 /*A6*/
77};
78
79/**
80 * Initializes a test tone by picking a random but well-known frequency (in Hz).
81 *
82 * @returns Randomly picked frequency (in Hz).
83 * @param pTone Pointer to test tone to initialize.
84 * @param pProps PCM properties to use for the test tone.
85 */
86double AudioTestToneInitRandom(PAUDIOTESTTONE pTone, PPDMAUDIOPCMPROPS pProps)
87{
88 /* Pick a frequency from our selection, so that every time a recording starts
89 * we'll hopfully generate a different note. */
90 pTone->rdFreqHz = s_aAudioTestToneFreqsHz[RTRandU32Ex(0, RT_ELEMENTS(s_aAudioTestToneFreqsHz) - 1)];
91 pTone->rdFixed = 2.0 * M_PI * pTone->rdFreqHz / PDMAudioPropsHz(pProps);
92 pTone->uSample = 0;
93
94 memcpy(&pTone->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
95
96 pTone->enmType = AUDIOTESTTONETYPE_SINE; /* Only type implemented so far. */
97
98 return pTone->rdFreqHz;
99}
100
101/**
102 * Writes (and iterates) a given test tone to an output buffer.
103 *
104 * @returns VBox status code.
105 * @param pTone Pointer to test tone to write.
106 * @param pvBuf Pointer to output buffer to write test tone to.
107 * @param cbBuf Size (in bytes) of output buffer.
108 * @param pcbWritten How many bytes were written on success.
109 */
110int AudioTestToneWrite(PAUDIOTESTTONE pTone, void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
111{
112 /*
113 * Clear the buffer first so we don't need to thing about additional channels.
114 */
115 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pTone->Props, cbBuf);
116 PDMAudioPropsClearBuffer(&pTone->Props, pvBuf, cbBuf, cFrames);
117
118 /*
119 * Generate the select sin wave in the first channel:
120 */
121 uint32_t const cbFrame = PDMAudioPropsFrameSize(&pTone->Props);
122 double const rdFixed = pTone->rdFixed;
123 uint64_t iSrcFrame = pTone->uSample;
124 switch (PDMAudioPropsSampleSize(&pTone->Props))
125 {
126 case 1:
127 /* untested */
128 if (PDMAudioPropsIsSigned(&pTone->Props))
129 {
130 int8_t *piSample = (int8_t *)pvBuf;
131 while (cFrames-- > 0)
132 {
133 *piSample = (int8_t)(126 /*Amplitude*/ * sin(rdFixed * iSrcFrame));
134 iSrcFrame++;
135 piSample += cbFrame;
136 }
137 }
138 else
139 {
140 /* untested */
141 uint8_t *pbSample = (uint8_t *)pvBuf;
142 while (cFrames-- > 0)
143 {
144 *pbSample = (uint8_t)(126 /*Amplitude*/ * sin(rdFixed * iSrcFrame) + 0x80);
145 iSrcFrame++;
146 pbSample += cbFrame;
147 }
148 }
149 break;
150
151 case 2:
152 if (PDMAudioPropsIsSigned(&pTone->Props))
153 {
154 int16_t *piSample = (int16_t *)pvBuf;
155 while (cFrames-- > 0)
156 {
157 *piSample = (int16_t)(32760 /*Amplitude*/ * sin(rdFixed * iSrcFrame));
158 iSrcFrame++;
159 piSample = (int16_t *)((uint8_t *)piSample + cbFrame);
160 }
161 }
162 else
163 {
164 /* untested */
165 uint16_t *puSample = (uint16_t *)pvBuf;
166 while (cFrames-- > 0)
167 {
168 *puSample = (uint16_t)(32760 /*Amplitude*/ * sin(rdFixed * iSrcFrame) + 0x8000);
169 iSrcFrame++;
170 puSample = (uint16_t *)((uint8_t *)puSample + cbFrame);
171 }
172 }
173 break;
174
175 case 4:
176 /* untested */
177 if (PDMAudioPropsIsSigned(&pTone->Props))
178 {
179 int32_t *piSample = (int32_t *)pvBuf;
180 while (cFrames-- > 0)
181 {
182 *piSample = (int32_t)((32760 << 16) /*Amplitude*/ * sin(rdFixed * iSrcFrame));
183 iSrcFrame++;
184 piSample = (int32_t *)((uint8_t *)piSample + cbFrame);
185 }
186 }
187 else
188 {
189 uint32_t *puSample = (uint32_t *)pvBuf;
190 while (cFrames-- > 0)
191 {
192 *puSample = (uint32_t)((32760 << 16) /*Amplitude*/ * sin(rdFixed * iSrcFrame) + UINT32_C(0x80000000));
193 iSrcFrame++;
194 puSample = (uint32_t *)((uint8_t *)puSample + cbFrame);
195 }
196 }
197 break;
198
199 default:
200 AssertFailedReturn(VERR_NOT_SUPPORTED);
201 }
202
203 pTone->uSample = iSrcFrame;
204
205 if (pcbWritten)
206 *pcbWritten = PDMAudioPropsFramesToBytes(&pTone->Props, cFrames);
207
208 return VINF_SUCCESS;
209}
210
211/**
212 * Initializes an audio test tone parameters struct with random values.
213 * @param pToneParams Test tone parameters to initialize.
214 * @param pProps PCM properties to use for the test tone.
215 */
216int AudioTestToneParamsInitRandom(PAUDIOTESTTONEPARMS pToneParams, PPDMAUDIOPCMPROPS pProps)
217{
218 AssertReturn(PDMAudioPropsAreValid(pProps), VERR_INVALID_PARAMETER);
219
220 memcpy(&pToneParams->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
221
222 /** @todo Make this a bit more sophisticated later, e.g. muting and prequel/sequel are not very balanced. */
223
224 pToneParams->msPrequel = RTRandU32Ex(0, RT_MS_5SEC);
225 pToneParams->msDuration = RTRandU32Ex(0, RT_MS_10SEC); /** @todo Probably a bit too long, but let's see. */
226 pToneParams->msSequel = RTRandU32Ex(0, RT_MS_5SEC);
227 pToneParams->uVolumePercent = RTRandU32Ex(0, 100);
228
229 return VINF_SUCCESS;
230}
231
232int AudioTestPathCreate(char *pszPath, size_t cbPath, const char *pszTag)
233{
234 int rc;
235
236 char szTag[RTUUID_STR_LENGTH + 1];
237 if (pszTag)
238 {
239 rc = RTStrCopy(szTag, sizeof(szTag), pszTag);
240 AssertRCReturn(rc, rc);
241 }
242 else /* Create an UUID if no tag is specified. */
243 {
244 RTUUID UUID;
245 rc = RTUuidCreate(&UUID);
246 AssertRCReturn(rc, rc);
247 rc = RTUuidToStr(&UUID, szTag, sizeof(szTag));
248 AssertRCReturn(rc, rc);
249 }
250
251 char szName[128];
252 if (RTStrPrintf2(szName, sizeof(szName), "%s%s", AUDIOTEST_PATH_PREFIX_STR, szTag) < 0)
253 AssertFailedReturn(VERR_BUFFER_OVERFLOW);
254
255 rc = RTPathAppend(pszPath, cbPath, szName);
256 AssertRCReturn(rc, rc);
257
258 char szTime[64];
259 RTTIMESPEC time;
260 if (!RTTimeSpecToString(RTTimeNow(&time), szTime, sizeof(szTime)))
261 return VERR_BUFFER_UNDERFLOW;
262
263 rc = RTPathAppend(pszPath, cbPath, szTime);
264 AssertRCReturn(rc, rc);
265
266 return RTDirCreateFullPath(pszPath, RTFS_UNIX_IRWXU);
267}
268
269static int audioTestManifestWriteV(PAUDIOTESTSET pSet, const char *pszFormat, va_list args)
270{
271 char *psz = NULL;
272 RTStrAPrintfV(&psz, pszFormat, args);
273 AssertPtr(psz);
274
275 /** @todo Use RTIniFileWrite once its implemented. */
276 int rc = RTFileWrite(pSet->f.hFile, psz, strlen(psz), NULL);
277 AssertRC(rc);
278
279 RTStrFree(psz);
280
281 return rc;
282}
283
284static int audioTestManifestWriteLn(PAUDIOTESTSET pSet, const char *pszFormat, ...)
285{
286 va_list va;
287 va_start(va, pszFormat);
288
289 int rc = audioTestManifestWriteV(pSet, pszFormat, va);
290 AssertRC(rc);
291
292 va_end(va);
293
294 /** @todo Keep it as simple as possible for now. Improve this later. */
295 rc = RTFileWrite(pSet->f.hFile, "\n", strlen("\n"), NULL);
296 AssertRC(rc);
297
298 return rc;
299}
300
301static int audioTestManifestWriteSection(PAUDIOTESTSET pSet, const char *pszFormat, ...)
302{
303 va_list va;
304 va_start(va, pszFormat);
305
306 /** @todo Keep it as simple as possible for now. Improve this later. */
307 int rc = RTFileWrite(pSet->f.hFile, "[", strlen("["), NULL);
308 AssertRC(rc);
309
310 rc = audioTestManifestWriteV(pSet, pszFormat, va);
311 AssertRC(rc);
312
313 rc = RTFileWrite(pSet->f.hFile, "]\n", strlen("]\n"), NULL);
314 AssertRC(rc);
315
316 va_end(va);
317
318 return rc;
319}
320
321static void audioTestSetInitInternal(PAUDIOTESTSET pSet)
322{
323 pSet->f.hFile = NIL_RTFILE;
324}
325
326static bool audioTestManifestIsOpen(PAUDIOTESTSET pSet)
327{
328 if ( pSet->enmMode == AUDIOTESTSETMODE_TEST
329 && pSet->f.hFile != NIL_RTFILE)
330 return true;
331 else if ( pSet->enmMode == AUDIOTESTSETMODE_VERIFY
332 && pSet->f.hIniFile != NIL_RTINIFILE)
333 return true;
334
335 return false;
336}
337
338static void audioTestErrorDescInit(PAUDIOTESTERRORDESC pErr)
339{
340 RTListInit(&pErr->List);
341 pErr->cErrors = 0;
342}
343
344void AudioTestErrorDescDestroy(PAUDIOTESTERRORDESC pErr)
345{
346 if (!pErr)
347 return;
348
349 PAUDIOTESTERRORENTRY pErrEntry, pErrEntryNext;
350 RTListForEachSafe(&pErr->List, pErrEntry, pErrEntryNext, AUDIOTESTERRORENTRY, Node)
351 {
352 RTListNodeRemove(&pErrEntry->Node);
353
354 RTMemFree(pErrEntry);
355
356 Assert(pErr->cErrors);
357 pErr->cErrors--;
358 }
359
360 Assert(pErr->cErrors == 0);
361}
362
363bool AudioTestErrorDescFailed(PAUDIOTESTERRORDESC pErr)
364{
365 if (pErr->cErrors)
366 {
367 Assert(!RTListIsEmpty(&pErr->List));
368 return true;
369 }
370
371 return false;
372}
373
374static int audioTestErrorDescAddV(PAUDIOTESTERRORDESC pErr, int rc, const char *pszFormat, va_list args)
375{
376 PAUDIOTESTERRORENTRY pEntry = (PAUDIOTESTERRORENTRY)RTMemAlloc(sizeof(AUDIOTESTERRORENTRY));
377 AssertReturn(pEntry, VERR_NO_MEMORY);
378
379 if (RTStrPrintf2V(pEntry->szDesc, sizeof(pEntry->szDesc), pszFormat, args) < 0)
380 AssertFailedReturn(VERR_BUFFER_OVERFLOW);
381
382 pEntry->rc = rc;
383
384 RTListAppend(&pErr->List, &pEntry->Node);
385
386 pErr->cErrors++;
387
388 return VINF_SUCCESS;
389}
390
391static int audioTestErrorDescAdd(PAUDIOTESTERRORDESC pErr, const char *pszFormat, ...)
392{
393 va_list va;
394 va_start(va, pszFormat);
395
396 int rc = audioTestErrorDescAddV(pErr, VERR_GENERAL_FAILURE /** @todo Fudge! */, pszFormat, va);
397
398 va_end(va);
399 return rc;
400}
401
402#if 0
403static int audioTestErrorDescAddRc(PAUDIOTESTERRORDESC pErr, int rc, const char *pszFormat, ...)
404{
405 va_list va;
406 va_start(va, pszFormat);
407
408 int rc2 = audioTestErrorDescAddV(pErr, rc, pszFormat, va);
409
410 va_end(va);
411 return rc2;
412}
413#endif
414
415int AudioTestPathCreateTemp(char *pszPath, size_t cbPath, const char *pszTag)
416{
417 int rc = RTPathTemp(pszPath, cbPath);
418 AssertRCReturn(rc, rc);
419 rc = AudioTestPathCreate(pszPath, cbPath, pszTag);
420 AssertRCReturn(rc, rc);
421
422 return rc;
423}
424
425int AudioTestSetCreate(PAUDIOTESTSET pSet, const char *pszPath, const char *pszTag)
426{
427 int rc;
428
429 audioTestSetInitInternal(pSet);
430
431 if (pszPath)
432 {
433 rc = RTStrCopy(pSet->szPathAbs, sizeof(pSet->szPathAbs), pszPath);
434 AssertRCReturn(rc, rc);
435
436 rc = AudioTestPathCreate(pSet->szPathAbs, sizeof(pSet->szPathAbs), pszTag);
437 }
438 else
439 rc = AudioTestPathCreateTemp(pSet->szPathAbs, sizeof(pSet->szPathAbs), pszTag);
440 AssertRCReturn(rc, rc);
441
442 if (RT_SUCCESS(rc))
443 {
444 char szManifest[RTPATH_MAX];
445 rc = RTStrCopy(szManifest, sizeof(szManifest), pszPath);
446 AssertRCReturn(rc, rc);
447
448 rc = RTPathAppend(szManifest, sizeof(szManifest), AUDIOTEST_MANIFEST_FILE_STR);
449 AssertRCReturn(rc, rc);
450
451 rc = RTFileOpen(&pSet->f.hFile, szManifest,
452 RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
453 AssertRCReturn(rc, rc);
454
455 rc = audioTestManifestWriteSection(pSet, "header");
456 AssertRCReturn(rc, rc);
457
458 rc = audioTestManifestWriteLn(pSet, "magic=vkat_ini"); /* VKAT Manifest, .INI-style. */
459 AssertRCReturn(rc, rc);
460 rc = audioTestManifestWriteLn(pSet, "ver=%d", AUDIOTEST_MANIFEST_VER);
461 AssertRCReturn(rc, rc);
462 rc = audioTestManifestWriteLn(pSet, "tag=%s", pszTag);
463 AssertRCReturn(rc, rc);
464
465 char szVal[64];
466 RTTIMESPEC time;
467 if (!RTTimeSpecToString(RTTimeNow(&time), szVal, sizeof(szVal)))
468 AssertFailedReturn(VERR_BUFFER_OVERFLOW);
469
470 rc = audioTestManifestWriteLn(pSet, "date_created=%s", szVal);
471 AssertRCReturn(rc, rc);
472
473 rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szVal, sizeof(szVal));
474 AssertRCReturn(rc, rc);
475 rc = audioTestManifestWriteLn(pSet, "os_product=%s", szVal);
476 AssertRCReturn(rc, rc);
477 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szVal, sizeof(szVal));
478 AssertRCReturn(rc, rc);
479 rc = audioTestManifestWriteLn(pSet, "os_rel=%s", szVal);
480 AssertRCReturn(rc, rc);
481 rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szVal, sizeof(szVal));
482 AssertRCReturn(rc, rc);
483 rc = audioTestManifestWriteLn(pSet, "os_ver=%s", szVal);
484 AssertRCReturn(rc, rc);
485
486 rc = audioTestManifestWriteLn(pSet, "vbox_ver=%s r%u %s (%s %s)",
487 VBOX_VERSION_STRING, RTBldCfgRevision(),
488 RTBldCfgTargetDotArch(), __DATE__, __TIME__);
489 AssertRCReturn(rc, rc);
490
491 pSet->enmMode = AUDIOTESTSETMODE_TEST;
492 }
493
494 return rc;
495}
496
497void AudioTestSetDestroy(PAUDIOTESTSET pSet)
498{
499 if (!pSet)
500 return;
501
502 if (RTFileIsValid(pSet->f.hFile))
503 {
504 RTFileClose(pSet->f.hFile);
505 pSet->f.hFile = NIL_RTFILE;
506 }
507}
508
509int AudioTestSetOpen(PAUDIOTESTSET pSet, const char *pszPath)
510{
511 audioTestSetInitInternal(pSet);
512
513 char szManifest[RTPATH_MAX];
514 int rc = RTStrCopy(szManifest, sizeof(szManifest), pszPath);
515 AssertRCReturn(rc, rc);
516
517 rc = RTPathAppend(szManifest, sizeof(szManifest), AUDIOTEST_MANIFEST_FILE_STR);
518 AssertRCReturn(rc, rc);
519
520 RTVFSFILE hVfsFile;
521 rc = RTVfsFileOpenNormal(szManifest, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, &hVfsFile);
522 if (RT_FAILURE(rc))
523 return rc;
524
525 rc = RTIniFileCreateFromVfsFile(&pSet->f.hIniFile, hVfsFile, RTINIFILE_F_READONLY);
526 RTVfsFileRelease(hVfsFile);
527 AssertRCReturn(rc, rc);
528
529 pSet->enmMode = AUDIOTESTSETMODE_VERIFY;
530
531 return rc;
532}
533
534void AudioTestSetClose(PAUDIOTESTSET pSet)
535{
536 AudioTestSetDestroy(pSet);
537}
538
539int AudioTestSetPack(PAUDIOTESTSET pSet, const char *pszOutDir)
540{
541 RT_NOREF(pSet, pszOutDir);
542
543 AssertReturn(audioTestManifestIsOpen(pSet), VERR_WRONG_ORDER);
544 // RTZipTarCmd()
545
546 return VERR_NOT_IMPLEMENTED;
547}
548
549int AudioTestSetUnpack(const char *pszFile, const char *pszOutDir)
550{
551 RT_NOREF(pszFile, pszOutDir);
552
553 // RTZipTarCmd()
554
555 return VERR_NOT_IMPLEMENTED;
556}
557
558int AudioTestSetVerify(PAUDIOTESTSET pSet, const char *pszTag, PAUDIOTESTERRORDESC pErrDesc)
559{
560 AssertReturn(audioTestManifestIsOpen(pSet), VERR_WRONG_ORDER);
561
562 /* We ASSUME the caller has not init'd pErrDesc. */
563 audioTestErrorDescInit(pErrDesc);
564
565 char szVal[_1K]; /** @todo Enough, too much? */
566
567 int rc2 = RTIniFileQueryValue(pSet->f.hIniFile, AUDIOTEST_INI_SEC_HDR_STR, "tag", szVal, sizeof(szVal), NULL);
568 if ( RT_FAILURE(rc2)
569 || RTStrICmp(pszTag, szVal))
570 audioTestErrorDescAdd(pErrDesc, "Tag '%s' does not match with manifest's tag '%s'", pszTag, szVal);
571
572 /* Only return critical stuff not related to actual testing here. */
573 return VINF_SUCCESS;
574}
575
Note: See TracBrowser for help on using the repository browser.

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