mirror of https://github.com/mackron/dr_libs
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
8.6 KiB
C
264 lines
8.6 KiB
C
/*
|
|
This test is mainly just to check basic functionality is working at a basic level without crashing. One thing
|
|
in particular it checks is consistency in the output when opening with different modes.
|
|
|
|
When a file is opened from a memory buffer, dr_mp3 will take a different path for decoding which is optimized
|
|
to reduce data movement. This test will ensure that the output between callback based decoding and memory
|
|
buffer decoding is consistent.
|
|
|
|
Another thing this will check is that opening with the `_with_metadata()` variants results in consistent
|
|
output. The reason this is necessary is because tags are skipped in slightly different ways depending on
|
|
whether or not a metadata callback is provided.
|
|
*/
|
|
#include "mp3_common.c"
|
|
|
|
#define FILE_NAME_WIDTH 40
|
|
#define NUMBER_WIDTH 10
|
|
#define TABLE_MARGIN 2
|
|
|
|
void on_meta(void* pUserData, const drmp3_metadata* pMetadata)
|
|
{
|
|
(void)pUserData;
|
|
(void)pMetadata;
|
|
}
|
|
|
|
void* open_decoders(drmp3* pDecoderMemory, drmp3* pDecoderMemoryMD, drmp3* pDecoderFile, drmp3* pDecoderFileMD, const char* pFilePath, size_t* pFileSize)
|
|
{
|
|
size_t dataSize;
|
|
void* pData;
|
|
|
|
/* Initialize the memory decoder. */
|
|
pData = dr_open_and_read_file(pFilePath, &dataSize);
|
|
if (pData == NULL) {
|
|
printf("Failed to open file \"%s\"\n", pFilePath);
|
|
return NULL;
|
|
}
|
|
|
|
if (!drmp3_init_memory_with_metadata(pDecoderMemory, pData, dataSize, NULL, NULL, NULL)) {
|
|
free(pData);
|
|
printf("Failed to init MP3 decoder \"%s\"\n", pFilePath);
|
|
return NULL;
|
|
}
|
|
|
|
if (!drmp3_init_memory_with_metadata(pDecoderMemoryMD, pData, dataSize, on_meta, NULL, NULL)) {
|
|
drmp3_uninit(pDecoderMemory);
|
|
free(pData);
|
|
printf("Failed to init MP3 decoder \"%s\"\n", pFilePath);
|
|
return NULL;
|
|
}
|
|
|
|
/* Initialize the file decoder. */
|
|
if (!drmp3_init_file_with_metadata(pDecoderFile, pFilePath, NULL, NULL, NULL)) {
|
|
drmp3_uninit(pDecoderMemory);
|
|
free(pData);
|
|
printf("Failed to open file \"%s\"\n", pFilePath);
|
|
return NULL;
|
|
}
|
|
|
|
if (!drmp3_init_file_with_metadata(pDecoderFileMD, pFilePath, on_meta, NULL, NULL)) {
|
|
drmp3_uninit(pDecoderMemory);
|
|
drmp3_uninit(pDecoderFile);
|
|
free(pData);
|
|
printf("Failed to open file \"%s\"\n", pFilePath);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
There was a bug once where drmp3_get_pcm_frame_count() would put the decoder into a bad state, but would
|
|
only happen when the decoder was initialized from callbacks or a file. The code below will trigger this
|
|
code path.
|
|
*/
|
|
{
|
|
drmp3_uint64 totalFrameCount = drmp3_get_pcm_frame_count(pDecoderFile); /* <-- This needs to be the file decoder, not the memory decoder. */
|
|
(void)totalFrameCount;
|
|
}
|
|
|
|
*pFileSize = dataSize;
|
|
|
|
return pData;
|
|
}
|
|
|
|
int validate_basic_properties(drmp3* pMP3Memory, drmp3* pMP3MemoryMD, drmp3* pMP3File, drmp3* pMP3FileMD)
|
|
{
|
|
if (pMP3Memory->channels != pMP3File->channels || pMP3Memory->channels != pMP3MemoryMD->channels || pMP3Memory->channels != pMP3FileMD->channels) {
|
|
printf("Channel counts differ\n");
|
|
return 1;
|
|
}
|
|
|
|
if (pMP3Memory->sampleRate != pMP3File->sampleRate || pMP3Memory->sampleRate != pMP3MemoryMD->sampleRate || pMP3Memory->sampleRate != pMP3FileMD->sampleRate) {
|
|
printf("Sample rates differ\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int validate_decoding(drmp3* pMP3Memory, drmp3* pMP3MemoryMD, drmp3* pMP3File, drmp3* pMP3FileMD)
|
|
{
|
|
int result = 0;
|
|
|
|
for (;;) {
|
|
drmp3_uint64 iSample;
|
|
drmp3_uint64 pcmFrameCountMemory;
|
|
drmp3_uint64 pcmFrameCountMemoryMD;
|
|
drmp3_uint64 pcmFrameCountFile;
|
|
drmp3_uint64 pcmFrameCountFileMD;
|
|
drmp3_int16 pcmFramesMemory[4096];
|
|
drmp3_int16 pcmFramesMemoryMD[4096];
|
|
drmp3_int16 pcmFramesFile[4096];
|
|
drmp3_int16 pcmFramesFileMD[4096];
|
|
|
|
pcmFrameCountMemory = drmp3_read_pcm_frames_s16(pMP3Memory, DRMP3_COUNTOF(pcmFramesMemory) / pMP3Memory->channels, pcmFramesMemory);
|
|
pcmFrameCountMemoryMD = drmp3_read_pcm_frames_s16(pMP3MemoryMD, DRMP3_COUNTOF(pcmFramesMemoryMD) / pMP3MemoryMD->channels, pcmFramesMemoryMD);
|
|
pcmFrameCountFile = drmp3_read_pcm_frames_s16(pMP3File, DRMP3_COUNTOF(pcmFramesFile) / pMP3File->channels, pcmFramesFile);
|
|
pcmFrameCountFileMD = drmp3_read_pcm_frames_s16(pMP3FileMD, DRMP3_COUNTOF(pcmFramesFileMD) / pMP3FileMD->channels, pcmFramesFileMD);
|
|
|
|
/* Check the frame count first. */
|
|
if (pcmFrameCountMemory != pcmFrameCountFile) {
|
|
printf("Frame counts differ between memory and file: memory = %d; file = %d\n", (int)pcmFrameCountMemory, (int)pcmFrameCountFile);
|
|
result = 1;
|
|
break;
|
|
}
|
|
|
|
if (pcmFrameCountMemory != pcmFrameCountMemoryMD) {
|
|
printf("Frame counts differ when loading from memory without metadata: memory = %d; memory with metadata = %d\n", (int)pcmFrameCountMemory, (int)pcmFrameCountMemoryMD);
|
|
result = 1;
|
|
break;
|
|
}
|
|
|
|
if (pcmFrameCountFile != pcmFrameCountFileMD) {
|
|
printf("Frame counts differ when loading from file without metadata: file = %d; file with metadata = %d\n", (int)pcmFrameCountFile, (int)pcmFrameCountFileMD);
|
|
result = 1;
|
|
break;
|
|
}
|
|
|
|
/* Check individual frames. */
|
|
DRMP3_ASSERT(pcmFrameCountMemory == pcmFrameCountFile);
|
|
for (iSample = 0; iSample < pcmFrameCountMemory * pMP3Memory->channels; iSample += 1) {
|
|
if (pcmFramesMemory[iSample] != pcmFramesFile[iSample]) {
|
|
printf("Samples differ between memory and file: memory = %d; file = %d\n", (int)pcmFramesMemory[iSample], (int)pcmFramesFile[iSample]);
|
|
result = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We've reached the end if we didn't return any PCM frames. */
|
|
if (pcmFrameCountMemory == 0 || pcmFrameCountMemoryMD || pcmFrameCountFile == 0 || pcmFrameCountFileMD) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int test_file_inner(const char* pFilePath)
|
|
{
|
|
int result = 0;
|
|
drmp3 mp3Memory;
|
|
drmp3 mp3MemoryMD;
|
|
drmp3 mp3File;
|
|
drmp3 mp3FileMD;
|
|
size_t dataSize;
|
|
void* pData;
|
|
|
|
/* Open the decoders. This will print the relevant error message. */
|
|
pData = open_decoders(&mp3Memory, &mp3MemoryMD, &mp3File, &mp3FileMD, pFilePath, &dataSize);
|
|
if (pData == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
result = validate_basic_properties(&mp3Memory, &mp3MemoryMD, &mp3File, &mp3FileMD);
|
|
if (result != 0) {
|
|
goto done;
|
|
}
|
|
|
|
result = validate_decoding(&mp3Memory, &mp3MemoryMD, &mp3File, &mp3FileMD);
|
|
if (result != 0) {
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
drmp3_uninit(&mp3File);
|
|
drmp3_uninit(&mp3Memory);
|
|
free(pData);
|
|
return result;
|
|
}
|
|
|
|
int test_file(const char* pFilePath)
|
|
{
|
|
int result = 0;
|
|
drmp3_bool32 hasError = DRMP3_FALSE;
|
|
|
|
/*
|
|
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
|
|
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
|
|
from a file.
|
|
*/
|
|
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
|
|
|
|
result = test_file_inner(pFilePath);
|
|
if (result != 0) {
|
|
hasError = DRMP3_TRUE;
|
|
}
|
|
|
|
if (hasError) {
|
|
printf(" ERROR\n");
|
|
} else {
|
|
printf(" OK\n");
|
|
}
|
|
|
|
if (hasError) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int test_directory(const char* pDirectoryPath)
|
|
{
|
|
dr_file_iterator iteratorState;
|
|
dr_file_iterator* pFile;
|
|
drmp3_bool32 hasError = DRMP3_FALSE;
|
|
|
|
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
|
|
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
|
|
printf("\n");
|
|
|
|
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
|
|
if (pFile == NULL) {
|
|
printf("Failed to open directory \"%s\"\n", pDirectoryPath);
|
|
return 1;
|
|
}
|
|
|
|
while (pFile != NULL) {
|
|
int result;
|
|
|
|
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
|
|
if (!pFile->isDirectory) {
|
|
result = test_file(pFile->absolutePath);
|
|
if (result != 0) {
|
|
hasError = DRMP3_TRUE;
|
|
}
|
|
}
|
|
|
|
pFile = dr_file_iterator_next(pFile);
|
|
}
|
|
|
|
if (hasError) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
const char* pTestsFolder = "tests/testvectors/mp3/tests";
|
|
|
|
if (argc >= 2) {
|
|
pTestsFolder = argv[1];
|
|
}
|
|
|
|
return test_directory(pTestsFolder);
|
|
}
|