From a4ddf17998242976ba00b49c65c8f842594d15df Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 25 Mar 2018 16:28:01 +1000 Subject: [PATCH] Format converter bug fixes. --- mini_al.h | 29 +++--- tests/mal_test_0.c | 222 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 230 insertions(+), 21 deletions(-) diff --git a/mini_al.h b/mini_al.h index 881105b2..da60a061 100644 --- a/mini_al.h +++ b/mini_al.h @@ -16724,9 +16724,9 @@ mal_uint64 mal_format_converter_read_frames(mal_format_converter* pConverter, ma mal_uint64 totalFramesRead = 0; mal_uint32 sampleSizeIn = mal_get_bytes_per_sample(pConverter->config.formatIn); - //mal_uint32 sampleSizeOut = mal_get_bytes_per_sample(pConverter->config.formatOut); - mal_uint32 frameSizeIn = sampleSizeIn * pConverter->config.channels; - //mal_uint32 frameSizeOut = sampleSizeOut * pConverter->config.channels; + mal_uint32 sampleSizeOut = mal_get_bytes_per_sample(pConverter->config.formatOut); + //mal_uint32 frameSizeIn = sampleSizeIn * pConverter->config.channels; + mal_uint32 frameSizeOut = sampleSizeOut * pConverter->config.channels; mal_uint8* pNextFramesOut = (mal_uint8*)pFramesOut; if (pConverter->onRead != NULL) { @@ -16746,7 +16746,7 @@ mal_uint64 mal_format_converter_read_frames(mal_format_converter* pConverter, ma } totalFramesRead += framesJustRead; - pNextFramesOut += framesJustRead * frameSizeIn; + pNextFramesOut += framesJustRead * frameSizeOut; } } else { // Conversion required. @@ -16770,7 +16770,7 @@ mal_uint64 mal_format_converter_read_frames(mal_format_converter* pConverter, ma pConverter->onConvertPCM(pNextFramesOut, temp, framesJustRead*pConverter->config.channels, pConverter->config.ditherMode); totalFramesRead += framesJustRead; - pNextFramesOut += framesJustRead * frameSizeIn; + pNextFramesOut += framesJustRead * frameSizeOut; } } } else { @@ -16823,7 +16823,7 @@ mal_uint64 mal_format_converter_read_frames(mal_format_converter* pConverter, ma pConverter->onInterleavePCM(pNextFramesOut, (const void**)ppTempSampleOfOutFormat, framesJustRead, pConverter->config.channels); totalFramesRead += framesJustRead; - pNextFramesOut += framesJustRead * frameSizeIn; + pNextFramesOut += framesJustRead * frameSizeOut; } } @@ -16838,7 +16838,7 @@ mal_uint64 mal_format_converter_read_frames_separated(mal_format_converter* pCon mal_uint64 totalFramesRead = 0; mal_uint32 sampleSizeIn = mal_get_bytes_per_sample(pConverter->config.formatIn); - //mal_uint32 sampleSizeOut = mal_get_bytes_per_sample(pConverter->config.formatOut); + mal_uint32 sampleSizeOut = mal_get_bytes_per_sample(pConverter->config.formatOut); mal_uint8* ppNextSamplesOut[MAL_MAX_CHANNELS]; mal_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pConverter->config.channels); @@ -16881,7 +16881,7 @@ mal_uint64 mal_format_converter_read_frames_separated(mal_format_converter* pCon totalFramesRead += framesJustRead; for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) { - ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeIn; + ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut; } } } else { @@ -16902,7 +16902,7 @@ mal_uint64 mal_format_converter_read_frames_separated(mal_format_converter* pCon totalFramesRead += framesJustRead; for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) { - ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeIn; + ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut; } } } else { @@ -16910,6 +16910,11 @@ mal_uint64 mal_format_converter_read_frames_separated(mal_format_converter* pCon mal_uint8 temp[MAL_MAX_CHANNELS][MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; mal_assert(sizeof(temp[0]) <= 0xFFFFFFFF); + void* ppTemp[MAL_MAX_CHANNELS]; + for (mal_uint32 i = 0; i < pConverter->config.channels; ++i) { + ppTemp[i] = &temp[i]; + } + mal_uint32 maxFramesToReadAtATime = sizeof(temp[0]) / sampleSizeIn; while (totalFramesRead < frameCount) { @@ -16919,14 +16924,14 @@ mal_uint64 mal_format_converter_read_frames_separated(mal_format_converter* pCon framesToReadRightNow = maxFramesToReadAtATime; } - mal_uint32 framesJustRead = (mal_uint32)pConverter->onReadSeparated(pConverter, (mal_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pConverter->pUserData); + mal_uint32 framesJustRead = (mal_uint32)pConverter->onReadSeparated(pConverter, (mal_uint32)framesToReadRightNow, ppTemp, pConverter->pUserData); if (framesJustRead == 0) { break; } for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) { - pConverter->onConvertPCM(ppNextSamplesOut[iChannel], temp[iChannel], framesJustRead, pConverter->config.ditherMode); - ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeIn; + pConverter->onConvertPCM(ppNextSamplesOut[iChannel], ppTemp[iChannel], framesJustRead, pConverter->config.ditherMode); + ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut; } totalFramesRead += framesJustRead; diff --git a/tests/mal_test_0.c b/tests/mal_test_0.c index 5ec802c5..5b8236dc 100644 --- a/tests/mal_test_0.c +++ b/tests/mal_test_0.c @@ -34,6 +34,22 @@ void on_log(mal_context* pContext, mal_device* pDevice, const char* message) printf("%s\n", message); } +FILE* mal_fopen(const char* filePath, const char* openMode) +{ + FILE* pFile; +#if _MSC_VER + if (fopen_s(&pFile, filePath, openMode) != 0) { + return NULL; + } +#else + pFile = fopen(filePath, openMode); + if (pFile == NULL) { + return NULL; + } +#endif + + return pFile; +} void* open_and_read_file_data(const char* filePath, size_t* pSizeOut) { @@ -44,17 +60,10 @@ void* open_and_read_file_data(const char* filePath, size_t* pSizeOut) return NULL; } - FILE* pFile; -#if _MSC_VER - if (fopen_s(&pFile, filePath, "rb") != 0) { - return NULL; - } -#else - pFile = fopen(filePath, "rb"); + FILE* pFile = mal_fopen(filePath, "rb"); if (pFile == NULL) { return NULL; } -#endif fseek(pFile, 0, SEEK_END); mal_uint64 fileSize = ftell(pFile); @@ -168,7 +177,7 @@ int mal_pcm_compare(const void* a, const void* b, mal_uint64 count, mal_format f case mal_format_s24: { mal_int32 sampleA = ((mal_int32)(((mal_uint32)(a_u8[i*3+0]) << 8) | ((mal_uint32)(a_u8[i*3+1]) << 16) | ((mal_uint32)(a_u8[i*3+2])) << 24)) >> 8; - mal_int32 sampleB = ((mal_int32)(((mal_uint32)(b_u8[i*3+0]) << 8) | ((mal_uint32)(b_u8[i*3+1]) << 16) | ((mal_uint32)(b_u8[i*3+2])) << 24)) >> 8;; + mal_int32 sampleB = ((mal_int32)(((mal_uint32)(b_u8[i*3+0]) << 8) | ((mal_uint32)(b_u8[i*3+1]) << 16) | ((mal_uint32)(b_u8[i*3+2])) << 24)) >> 8; if (sampleA != sampleB) { if (abs(sampleA - sampleB) > allowedDifference) { // Allow a difference of 1. printf("Sample %I64u not equal. %d != %d (diff: %d)\n", i, sampleA, sampleB, sampleA - sampleB); @@ -936,6 +945,192 @@ int do_interleaving_tests() } +mal_uint32 converter_test_interleaved_callback(mal_format_converter* pConverter, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + mal_sine_wave* pSineWave = (mal_sine_wave*)pUserData; + mal_assert(pSineWave != NULL); + + float* pFramesOutF32 = (float*)pFramesOut; + + for (mal_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { + float sample; + mal_sine_wave_read(pSineWave, 1, &sample); + + for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pConverter->config.channels + iChannel] = sample; + } + } + + return frameCount; +} + +mal_uint32 converter_test_separated_callback(mal_format_converter* pConverter, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData) +{ + mal_sine_wave* pSineWave = (mal_sine_wave*)pUserData; + mal_assert(pSineWave != NULL); + + mal_sine_wave_read(pSineWave, frameCount, ppSamplesOut[0]); + + // Copy everything from the first channel over the others. + for (mal_uint32 iChannel = 1; iChannel < pConverter->config.channels; iChannel += 1) { + mal_copy_memory(ppSamplesOut[iChannel], ppSamplesOut[0], frameCount * sizeof(float)); + } + + return frameCount; +} + +int do_format_converter_tests() +{ + double amplitude = 1; + double periodsPerSecond = 400; + mal_uint32 sampleRate = 48000; + + mal_result result = MAL_SUCCESS; + + mal_sine_wave sineWave; + mal_format_converter converter; + + mal_format_converter_config config; + mal_zero_object(&config); + config.formatIn = mal_format_f32; + config.formatOut = mal_format_s16; + config.channels = 2; + config.streamFormatIn = mal_stream_format_pcm; + config.streamFormatOut = mal_stream_format_pcm; + config.ditherMode = mal_dither_mode_none; + + // Interleaved/Interleaved f32 to s16. + { + mal_sine_wave_init(amplitude, periodsPerSecond, sampleRate, &sineWave); + result = mal_format_converter_init(&config, converter_test_interleaved_callback, &sineWave, &converter); + if (result != MAL_SUCCESS) { + printf("Failed to initialize converter.\n"); + return -1; + } + + mal_int16 interleavedFrames[MAL_MAX_CHANNELS * 1024]; + mal_uint64 framesRead = mal_format_converter_read_frames(&converter, 1024, interleavedFrames); + if (framesRead != 1024) { + printf("Failed to read interleaved data from converter.\n"); + return -1; + } + + FILE* pFile = mal_fopen("res/output/converter_f32_to_s16_interleaved_interleaved__stereo_48000.raw", "wb"); + if (pFile == NULL) { + printf("Failed to open output file.\n"); + return -1; + } + + fwrite(interleavedFrames, sizeof(mal_int16), framesRead * converter.config.channels, pFile); + fclose(pFile); + } + + // Interleaved/Deinterleaved f32 to s16. + { + mal_sine_wave_init(amplitude, periodsPerSecond, sampleRate, &sineWave); + result = mal_format_converter_init(&config, converter_test_interleaved_callback, &sineWave, &converter); + if (result != MAL_SUCCESS) { + printf("Failed to initialize converter.\n"); + return -1; + } + + mal_int16 separatedFrames[MAL_MAX_CHANNELS][1024]; + void* ppSeparatedFrames[MAL_MAX_CHANNELS]; + for (mal_uint32 iChannel = 0; iChannel < converter.config.channels; iChannel += 1) { + ppSeparatedFrames[iChannel] = &separatedFrames[iChannel]; + } + + mal_uint64 framesRead = mal_format_converter_read_frames_separated(&converter, 1024, ppSeparatedFrames); + if (framesRead != 1024) { + printf("Failed to read interleaved data from converter.\n"); + return -1; + } + + // Write a separate file for each channel. + for (mal_uint32 iChannel = 0; iChannel < converter.config.channels; iChannel += 1) { + char filePath[256]; + snprintf(filePath, sizeof(filePath), "res/output/converter_f32_to_s16_interleaved_deinterleaved__stereo_48000.raw.%d", iChannel); + + FILE* pFile = mal_fopen(filePath, "wb"); + if (pFile == NULL) { + printf("Failed to open output file.\n"); + return -1; + } + + fwrite(ppSeparatedFrames[iChannel], sizeof(mal_int16), framesRead, pFile); + fclose(pFile); + } + } + + // Deinterleaved/Interleaved f32 to s16. + { + mal_sine_wave_init(amplitude, periodsPerSecond, sampleRate, &sineWave); + result = mal_format_converter_init_separated(&config, converter_test_separated_callback, &sineWave, &converter); + if (result != MAL_SUCCESS) { + printf("Failed to initialize converter.\n"); + return -1; + } + + mal_int16 interleavedFrames[MAL_MAX_CHANNELS * 1024]; + mal_uint64 framesRead = mal_format_converter_read_frames(&converter, 1024, interleavedFrames); + if (framesRead != 1024) { + printf("Failed to read interleaved data from converter.\n"); + return -1; + } + + FILE* pFile = mal_fopen("res/output/converter_f32_to_s16_deinterleaved_interleaved__stereo_48000.raw", "wb"); + if (pFile == NULL) { + printf("Failed to open output file.\n"); + return -1; + } + + fwrite(interleavedFrames, sizeof(mal_int16), framesRead * converter.config.channels, pFile); + fclose(pFile); + } + + // Deinterleaved/Deinterleaved f32 to s16. + { + mal_sine_wave_init(amplitude, periodsPerSecond, sampleRate, &sineWave); + result = mal_format_converter_init_separated(&config, converter_test_separated_callback, &sineWave, &converter); + if (result != MAL_SUCCESS) { + printf("Failed to initialize converter.\n"); + return -1; + } + + mal_int16 separatedFrames[MAL_MAX_CHANNELS][1024]; + void* ppSeparatedFrames[MAL_MAX_CHANNELS]; + for (mal_uint32 iChannel = 0; iChannel < converter.config.channels; iChannel += 1) { + ppSeparatedFrames[iChannel] = &separatedFrames[iChannel]; + } + + mal_uint64 framesRead = mal_format_converter_read_frames_separated(&converter, 1024, ppSeparatedFrames); + if (framesRead != 1024) { + printf("Failed to read interleaved data from converter.\n"); + return -1; + } + + // Write a separate file for each channel. + for (mal_uint32 iChannel = 0; iChannel < converter.config.channels; iChannel += 1) { + char filePath[256]; + snprintf(filePath, sizeof(filePath), "res/output/converter_f32_to_s16_deinterleaved_deinterleaved__stereo_48000.raw.%d", iChannel); + + FILE* pFile = mal_fopen(filePath, "wb"); + if (pFile == NULL) { + printf("Failed to open output file.\n"); + return -1; + } + + fwrite(ppSeparatedFrames[iChannel], sizeof(mal_int16), framesRead, pFile); + fclose(pFile); + } + } + + + + return 0; +} + + int do_backend_test(mal_backend backend) { mal_result result = MAL_SUCCESS; @@ -1182,6 +1377,15 @@ int main(int argc, char** argv) } printf("=== END TESTING INTERLEAVING/DEINTERLEAVING ===\n"); + printf("\n"); + + printf("=== TESTING FORMAT CONVERTER ===\n"); + result = do_format_converter_tests(); + if (result < 0) { + hasErrorOccurred = MAL_TRUE; + } + printf("=== END TESTING FORMAT CONVERTER ===\n"); + printf("\n"); printf("=== TESTING BACKENDS ===\n");