VirtualBox

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

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

Audio/ValKit: SCM fixes. bugref:10008

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