Skip to content

Commit 3f55f0e

Browse files
committed
dr_mp3: Add a basic validation test.
1 parent 62252db commit 3f55f0e

File tree

2 files changed

+258
-0
lines changed

2 files changed

+258
-0
lines changed

CMakeLists.txt

+5
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ if(NOT DR_LIBS_NO_MP3)
132132
if(DR_LIBS_BUILD_TESTS)
133133
enable_testing()
134134

135+
add_executable(mp3_basic tests/mp3/mp3_basic.c)
136+
target_include_directories(mp3_basic PRIVATE ${SNDFILE_INCLUDE_DIR})
137+
target_link_libraries(mp3_basic PRIVATE dl m pthread)
138+
add_test(NAME mp3_basic COMMAND mp3_basic ${CMAKE_CURRENT_SOURCE_DIR}/tests/testvectors/mp3/tests)
139+
135140
add_executable(mp3_playback tests/mp3/mp3_playback.c)
136141
target_include_directories(mp3_playback PRIVATE ${SNDFILE_INCLUDE_DIR})
137142
target_link_libraries(mp3_playback PRIVATE dl m pthread)

tests/mp3/mp3_basic.c

+253
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
This test is mainly just to check basic functionality is working at a basic level without crashing. One thing
3+
in particular it checks is consistency in the output when opening with different modes.
4+
5+
When a file is opened from a memory buffer, dr_mp3 will take a different path for decoding which is optimized
6+
to reduce data movement. This test will ensure that the output between callback based decoding and memory
7+
buffer decoding is consistent.
8+
9+
Another thing this will check is that opening with the `_with_metadata()` variants results in consistent
10+
output. The reason this is necessary is because tags are skipped in slightly different ways depending on
11+
whether or not a metadata callback is provided.
12+
*/
13+
#include "mp3_common.c"
14+
15+
#define FILE_NAME_WIDTH 40
16+
#define NUMBER_WIDTH 10
17+
#define TABLE_MARGIN 2
18+
19+
void on_meta(void* pUserData, const drmp3_metadata* pMetadata)
20+
{
21+
(void)pUserData;
22+
(void)pMetadata;
23+
}
24+
25+
void* open_decoders(drmp3* pDecoderMemory, drmp3* pDecoderMemoryMD, drmp3* pDecoderFile, drmp3* pDecoderFileMD, const char* pFilePath, size_t* pFileSize)
26+
{
27+
size_t dataSize;
28+
void* pData;
29+
30+
/* Initialize the memory decoder. */
31+
pData = dr_open_and_read_file(pFilePath, &dataSize);
32+
if (pData == NULL) {
33+
printf("Failed to open file \"%s\"\n", pFilePath);
34+
return NULL;
35+
}
36+
37+
if (!drmp3_init_memory_with_metadata(pDecoderMemory, pData, dataSize, NULL, NULL, NULL)) {
38+
free(pData);
39+
printf("Failed to init MP3 decoder \"%s\"\n", pFilePath);
40+
return NULL;
41+
}
42+
43+
if (!drmp3_init_memory_with_metadata(pDecoderMemoryMD, pData, dataSize, on_meta, NULL, NULL)) {
44+
drmp3_uninit(pDecoderMemory);
45+
free(pData);
46+
printf("Failed to init MP3 decoder \"%s\"\n", pFilePath);
47+
return NULL;
48+
}
49+
50+
/* Initialize the file decoder. */
51+
if (!drmp3_init_file_with_metadata(pDecoderFile, pFilePath, NULL, NULL, NULL)) {
52+
drmp3_uninit(pDecoderMemory);
53+
free(pData);
54+
printf("Failed to open file \"%s\"\n", pFilePath);
55+
return NULL;
56+
}
57+
58+
if (!drmp3_init_file_with_metadata(pDecoderFileMD, pFilePath, on_meta, NULL, NULL)) {
59+
drmp3_uninit(pDecoderMemory);
60+
drmp3_uninit(pDecoderFile);
61+
free(pData);
62+
printf("Failed to open file \"%s\"\n", pFilePath);
63+
return NULL;
64+
}
65+
66+
*pFileSize = dataSize;
67+
68+
return pData;
69+
}
70+
71+
int validate_basic_properties(drmp3* pMP3Memory, drmp3* pMP3MemoryMD, drmp3* pMP3File, drmp3* pMP3FileMD)
72+
{
73+
if (pMP3Memory->channels != pMP3File->channels || pMP3Memory->channels != pMP3MemoryMD->channels || pMP3Memory->channels != pMP3FileMD->channels) {
74+
printf("Channel counts differ\n");
75+
return 1;
76+
}
77+
78+
if (pMP3Memory->sampleRate != pMP3File->sampleRate || pMP3Memory->sampleRate != pMP3MemoryMD->sampleRate || pMP3Memory->sampleRate != pMP3FileMD->sampleRate) {
79+
printf("Sample rates differ\n");
80+
return 1;
81+
}
82+
83+
return 0;
84+
}
85+
86+
int validate_decoding(drmp3* pMP3Memory, drmp3* pMP3MemoryMD, drmp3* pMP3File, drmp3* pMP3FileMD)
87+
{
88+
int result = 0;
89+
90+
for (;;) {
91+
drmp3_uint64 iSample;
92+
drmp3_uint64 pcmFrameCountMemory;
93+
drmp3_uint64 pcmFrameCountMemoryMD;
94+
drmp3_uint64 pcmFrameCountFile;
95+
drmp3_uint64 pcmFrameCountFileMD;
96+
drmp3_int16 pcmFramesMemory[4096];
97+
drmp3_int16 pcmFramesMemoryMD[4096];
98+
drmp3_int16 pcmFramesFile[4096];
99+
drmp3_int16 pcmFramesFileMD[4096];
100+
101+
pcmFrameCountMemory = drmp3_read_pcm_frames_s16(pMP3Memory, DRMP3_COUNTOF(pcmFramesMemory) / pMP3Memory->channels, pcmFramesMemory);
102+
pcmFrameCountMemoryMD = drmp3_read_pcm_frames_s16(pMP3MemoryMD, DRMP3_COUNTOF(pcmFramesMemoryMD) / pMP3MemoryMD->channels, pcmFramesMemoryMD);
103+
pcmFrameCountFile = drmp3_read_pcm_frames_s16(pMP3File, DRMP3_COUNTOF(pcmFramesFile) / pMP3File->channels, pcmFramesFile);
104+
pcmFrameCountFileMD = drmp3_read_pcm_frames_s16(pMP3FileMD, DRMP3_COUNTOF(pcmFramesFileMD) / pMP3FileMD->channels, pcmFramesFileMD);
105+
106+
/* Check the frame count first. */
107+
if (pcmFrameCountMemory != pcmFrameCountFile) {
108+
printf("Frame counts differ between memory and file: memory = %d; file = %d\n", (int)pcmFrameCountMemory, (int)pcmFrameCountFile);
109+
result = 1;
110+
break;
111+
}
112+
113+
if (pcmFrameCountMemory != pcmFrameCountMemoryMD) {
114+
printf("Frame counts differ when loading from memory without metadata: memory = %d; memory with metadata = %d\n", (int)pcmFrameCountMemory, (int)pcmFrameCountMemoryMD);
115+
result = 1;
116+
break;
117+
}
118+
119+
if (pcmFrameCountFile != pcmFrameCountFileMD) {
120+
printf("Frame counts differ when loading from file without metadata: file = %d; file with metadata = %d\n", (int)pcmFrameCountFile, (int)pcmFrameCountFileMD);
121+
result = 1;
122+
break;
123+
}
124+
125+
/* Check individual frames. */
126+
DRMP3_ASSERT(pcmFrameCountMemory == pcmFrameCountFile);
127+
for (iSample = 0; iSample < pcmFrameCountMemory * pMP3Memory->channels; iSample += 1) {
128+
if (pcmFramesMemory[iSample] != pcmFramesFile[iSample]) {
129+
printf("Samples differ between memory and file: memory = %d; file = %d\n", (int)pcmFramesMemory[iSample], (int)pcmFramesFile[iSample]);
130+
result = 1;
131+
break;
132+
}
133+
}
134+
135+
/* We've reached the end if we didn't return any PCM frames. */
136+
if (pcmFrameCountMemory == 0 || pcmFrameCountMemoryMD || pcmFrameCountFile == 0 || pcmFrameCountFileMD) {
137+
break;
138+
}
139+
}
140+
141+
return result;
142+
}
143+
144+
int test_file_inner(const char* pFilePath)
145+
{
146+
int result = 0;
147+
drmp3 mp3Memory;
148+
drmp3 mp3MemoryMD;
149+
drmp3 mp3File;
150+
drmp3 mp3FileMD;
151+
size_t dataSize;
152+
void* pData;
153+
154+
/* Open the decoders. This will print the relevant error message. */
155+
pData = open_decoders(&mp3Memory, &mp3MemoryMD, &mp3File, &mp3FileMD, pFilePath, &dataSize);
156+
if (pData == NULL) {
157+
return 1;
158+
}
159+
160+
result = validate_basic_properties(&mp3Memory, &mp3MemoryMD, &mp3File, &mp3FileMD);
161+
if (result != 0) {
162+
goto done;
163+
}
164+
165+
result = validate_decoding(&mp3Memory, &mp3MemoryMD, &mp3File, &mp3FileMD);
166+
if (result != 0) {
167+
goto done;
168+
}
169+
170+
done:
171+
drmp3_uninit(&mp3File);
172+
drmp3_uninit(&mp3Memory);
173+
free(pData);
174+
return result;
175+
}
176+
177+
int test_file(const char* pFilePath)
178+
{
179+
int result = 0;
180+
drmp3_bool32 hasError = DRMP3_FALSE;
181+
182+
/*
183+
When opening from a memory buffer, dr_mp3 will take a different path for decoding which is optimized to reduce data movement. Since it's
184+
running on a separate path, we need to ensure it's returning consistent results with the other code path which will be used when decoding
185+
from a file.
186+
*/
187+
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
188+
189+
result = test_file_inner(pFilePath);
190+
if (result != 0) {
191+
hasError = DRMP3_TRUE;
192+
}
193+
194+
if (hasError) {
195+
printf(" ERROR\n");
196+
} else {
197+
printf(" OK\n");
198+
}
199+
200+
if (hasError) {
201+
return 1;
202+
} else {
203+
return 0;
204+
}
205+
}
206+
207+
int test_directory(const char* pDirectoryPath)
208+
{
209+
dr_file_iterator iteratorState;
210+
dr_file_iterator* pFile;
211+
drmp3_bool32 hasError = DRMP3_FALSE;
212+
213+
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
214+
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
215+
printf("\n");
216+
217+
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
218+
if (pFile == NULL) {
219+
printf("Failed to open directory \"%s\"\n", pDirectoryPath);
220+
return 1;
221+
}
222+
223+
while (pFile != NULL) {
224+
int result;
225+
226+
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
227+
if (!pFile->isDirectory) {
228+
result = test_file(pFile->absolutePath);
229+
if (result != 0) {
230+
hasError = DRMP3_TRUE;
231+
}
232+
}
233+
234+
pFile = dr_file_iterator_next(pFile);
235+
}
236+
237+
if (hasError) {
238+
return 1;
239+
} else {
240+
return 0;
241+
}
242+
}
243+
244+
int main(int argc, char** argv)
245+
{
246+
const char* pTestsFolder = "tests/testvectors/mp3/tests";
247+
248+
if (argc >= 2) {
249+
pTestsFolder = argv[1];
250+
}
251+
252+
return test_directory(pTestsFolder);
253+
}

0 commit comments

Comments
 (0)