diff --git a/research/ma_resampler.h b/research/ma_resampler.h index 0f56c4f3..60bfda8b 100644 --- a/research/ma_resampler.h +++ b/research/ma_resampler.h @@ -249,72 +249,6 @@ void ma_resampler_uninit(ma_resampler* pResampler) #endif } -static ma_result ma_resampler_process__seek__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pFrameCountOut != NULL) { - /* Seek by output frames. */ - if (pFramesIn != NULL) { - /* Read input data. */ - } else { - /* Don't read input data - just update timing and filter state as if zeroes were passed in. */ - } - } else { - /* Seek by input frames. */ - MA_ASSERT(pFrameCountIn != NULL); - - if (pFramesIn != NULL) { - /* Read input data. */ - } else { - /* Don't read input data - just update timing and filter state as if zeroes were passed in. */ - } - } - - return MA_SUCCESS; -} - -#if defined(MA_HAS_SPEEX_RESAMPLER) -static ma_result ma_resampler_process__seek__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) -{ - /* TODO: Implement me. */ - (void)pResampler; - (void)pFramesIn; - (void)pFrameCountIn; - (void)pFrameCountOut; - - return MA_INVALID_OPERATION; -} -#endif - -static ma_result ma_resampler_process__seek(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - switch (pResampler->config.algorithm) - { - case ma_resample_algorithm_linear: - { - return ma_resampler_process__seek__linear(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); - } break; - - case ma_resample_algorithm_speex: - { - #if defined(MA_HAS_SPEEX_RESAMPLER) - return ma_resampler_process__seek__speex(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); - #else - break; - #endif - }; - - default: break; - } - - /* Should never hit this. */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; -} - static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { ma_uint64 frameCountOut; @@ -603,6 +537,125 @@ static ma_result ma_resampler_process__read(ma_resampler* pResampler, const void return MA_INVALID_ARGS; } + +static ma_result ma_resampler_process__seek__generic(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) +{ + /* The generic seek method is implemented in on top of ma_resampler_process__read() by just processing into a dummy buffer. */ + float devnull[8192]; + ma_uint64 totalOutputFramesToProcess; + ma_uint64 totalOutputFramesProcessed; + ma_uint64 totalInputFramesProcessed; + ma_uint32 bpf; + ma_result result; + + MA_ASSERT(pResampler != NULL); + + totalOutputFramesProcessed = 0; + totalInputFramesProcessed = 0; + bpf = ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels); + + if (pFrameCountOut != NULL) { + /* Seek by output frames. */ + totalOutputFramesToProcess = *pFrameCountOut; + } else { + /* Seek by input frames. */ + MA_ASSERT(pFrameCountIn != NULL); + totalOutputFramesToProcess = ma_resampler_get_expected_output_frame_count(pResampler, *pFrameCountIn); + } + + if (pFramesIn != NULL) { + /* Process input data. */ + MA_ASSERT(pFrameCountIn != NULL); + while (totalOutputFramesProcessed < totalOutputFramesToProcess && totalInputFramesProcessed < *pFrameCountIn) { + ma_uint64 inputFramesToProcessThisIteration = (*pFrameCountIn - totalInputFramesProcessed); + ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed); + if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) { + outputFramesToProcessThisIteration = sizeof(devnull) / bpf; + } + + result = ma_resampler_process__read(pResampler, ma_offset_ptr(pFramesIn, totalInputFramesProcessed*bpf), &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + totalOutputFramesProcessed += outputFramesToProcessThisIteration; + totalInputFramesProcessed += inputFramesToProcessThisIteration; + } + } else { + /* Don't process input data - just update timing and filter state as if zeroes were passed in. */ + while (totalOutputFramesProcessed < totalOutputFramesToProcess) { + ma_uint64 inputFramesToProcessThisIteration = 16384; + ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed); + if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) { + outputFramesToProcessThisIteration = sizeof(devnull) / bpf; + } + + result = ma_resampler_process__read(pResampler, NULL, &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + totalOutputFramesProcessed += outputFramesToProcessThisIteration; + totalInputFramesProcessed += inputFramesToProcessThisIteration; + } + } + + + if (pFrameCountIn != NULL) { + *pFrameCountIn = totalInputFramesProcessed; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = totalOutputFramesProcessed; + } + + return MA_SUCCESS; +} + +static ma_result ma_resampler_process__seek__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) +{ + MA_ASSERT(pResampler != NULL); + + return ma_resampler_process__seek__generic(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); +} + +#if defined(MA_HAS_SPEEX_RESAMPLER) +static ma_result ma_resampler_process__seek__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) +{ + MA_ASSERT(pResampler != NULL); + + return ma_resampler_process__seek__generic(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); +} +#endif + +static ma_result ma_resampler_process__seek(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) +{ + MA_ASSERT(pResampler != NULL); + + switch (pResampler->config.algorithm) + { + case ma_resample_algorithm_linear: + { + return ma_resampler_process__seek__linear(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); + } break; + + case ma_resample_algorithm_speex: + { + #if defined(MA_HAS_SPEEX_RESAMPLER) + return ma_resampler_process__seek__speex(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); + #else + break; + #endif + }; + + default: break; + } + + /* Should never hit this. */ + MA_ASSERT(MA_FALSE); + return MA_INVALID_ARGS; +} + + ma_result ma_resampler_process(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { if (pResampler == NULL) { diff --git a/research/tests/ma_resampler_test_1.c b/research/tests/ma_resampler_test_1.c new file mode 100644 index 00000000..cf6c6447 --- /dev/null +++ b/research/tests/ma_resampler_test_1.c @@ -0,0 +1,317 @@ + +#define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION +#include "../../extras/speex_resampler/ma_speex_resampler.h" + +#define MA_DEBUG_OUTPUT +#define MINIAUDIO_IMPLEMENTATION +#include "../../miniaudio.h" +#include "../ma_resampler.h" + +int init_resampler(ma_uint32 rateIn, ma_uint32 rateOut, ma_resample_algorithm algorithm, ma_resampler* pResampler) +{ + ma_result result; + ma_resampler_config config; + + config = ma_resampler_config_init(ma_format_s16, 1, rateIn, rateOut, algorithm); + result = ma_resampler_init(&config, pResampler); + if (result != MA_SUCCESS) { + return (int)result; + } + + return 0; +} + +int do_count_query_test__required_input__fixed_interval(ma_resampler* pResampler, ma_uint64 frameCountPerIteration) +{ + int result = 0; + ma_int16 input[4096]; + ma_int16 i; + + MA_ASSERT(frameCountPerIteration < ma_countof(input)); + + /* Fill the input buffer with sequential numbers so we can get an idea on the state of things. Useful for inspecting the linear backend in particular. */ + for (i = 0; i < ma_countof(input); i += 1) { + input[i] = i; + } + + for (i = 0; i < ma_countof(input); i += (ma_int16)frameCountPerIteration) { + ma_int16 output[4096]; + ma_uint64 outputFrameCount; + ma_uint64 inputFrameCount; + ma_uint64 requiredInputFrameCount; + + /* We retrieve the required number of input frames for the specified number of output frames, and then compare with what we actually get when reading. */ + requiredInputFrameCount = ma_resampler_get_required_input_frame_count(pResampler, frameCountPerIteration); + + outputFrameCount = frameCountPerIteration; + inputFrameCount = ma_countof(input); + result = ma_resampler_process(pResampler, input, &inputFrameCount, output, &outputFrameCount); + if (result != MA_SUCCESS) { + printf("Failed to process frames."); + return result; + } + + if (inputFrameCount != requiredInputFrameCount) { + printf("ERROR: Predicted vs actual input count mismatch: predicted=%d, actual=%d\n", (int)requiredInputFrameCount, (int)inputFrameCount); + result = -1; + } + } + + if (result != 0) { + printf("FAILED\n"); + } else { + printf("PASSED\n"); + } + + return result; +} + +int do_count_query_test__required_input__by_algorithm_and_rate__fixed_interval(ma_resample_algorithm algorithm, ma_uint32 rateIn, ma_uint32 rateOut, ma_uint64 frameCountPerIteration) +{ + int result; + ma_resampler resampler; + + result = init_resampler(rateIn, rateOut, algorithm, &resampler); + if (result != 0) { + return 0; + } + + result = do_count_query_test__required_input__fixed_interval(&resampler, frameCountPerIteration); + + ma_resampler_uninit(&resampler); + return result; +} + +int do_count_query_test__required_input__by_algorithm__fixed_interval(ma_resample_algorithm algorithm, ma_uint64 frameCountPerIteration) +{ + int result; + + result = do_count_query_test__required_input__by_algorithm_and_rate__fixed_interval(algorithm, 44100, 48000, frameCountPerIteration); + if (result != 0) { + return result; + } + + result = do_count_query_test__required_input__by_algorithm_and_rate__fixed_interval(algorithm, 48000, 44100, frameCountPerIteration); + if (result != 0) { + return result; + } + + + result = do_count_query_test__required_input__by_algorithm_and_rate__fixed_interval(algorithm, 44100, 192000, frameCountPerIteration); + if (result != 0) { + return result; + } + + result = do_count_query_test__required_input__by_algorithm_and_rate__fixed_interval(algorithm, 192000, 44100, frameCountPerIteration); + if (result != 0) { + return result; + } + + return result; +} + +int do_count_query_tests__required_input__by_algorithm(ma_resample_algorithm algorithm) +{ + int result; + + result = do_count_query_test__required_input__by_algorithm__fixed_interval(algorithm, 1); + if (result != 0) { + return result; + } + + result = do_count_query_test__required_input__by_algorithm__fixed_interval(algorithm, 16); + if (result != 0) { + return result; + } + + result = do_count_query_test__required_input__by_algorithm__fixed_interval(algorithm, 127); + if (result != 0) { + return result; + } + + return 0; +} + +int do_count_query_tests__required_input() +{ + int result; + + printf("Linear\n"); + result = do_count_query_tests__required_input__by_algorithm(ma_resample_algorithm_linear); + if (result != 0) { + return result; + } + + printf("Speex\n"); + result = do_count_query_tests__required_input__by_algorithm(ma_resample_algorithm_speex); + if (result != 0) { + return result; + } + + return 0; +} + + + +int do_count_query_test__expected_output__fixed_interval(ma_resampler* pResampler, ma_uint64 frameCountPerIteration) +{ + int result = 0; + ma_int16 input[4096]; + ma_int16 i; + + MA_ASSERT(frameCountPerIteration < ma_countof(input)); + + /* Fill the input buffer with sequential numbers so we can get an idea on the state of things. Useful for inspecting the linear backend in particular. */ + for (i = 0; i < ma_countof(input); i += 1) { + input[i] = i; + } + + for (i = 0; i < ma_countof(input); i += (ma_int16)frameCountPerIteration) { + ma_int16 output[4096]; + ma_uint64 outputFrameCount; + ma_uint64 inputFrameCount; + ma_uint64 expectedOutputFrameCount; + + /* We retrieve the required number of input frames for the specified number of output frames, and then compare with what we actually get when reading. */ + expectedOutputFrameCount = ma_resampler_get_expected_output_frame_count(pResampler, frameCountPerIteration); + + outputFrameCount = ma_countof(output); + inputFrameCount = frameCountPerIteration; + result = ma_resampler_process(pResampler, input, &inputFrameCount, output, &outputFrameCount); + if (result != MA_SUCCESS) { + printf("Failed to process frames."); + return result; + } + + if (outputFrameCount != expectedOutputFrameCount) { + printf("ERROR: Predicted vs actual output count mismatch: predicted=%d, actual=%d\n", (int)expectedOutputFrameCount, (int)outputFrameCount); + result = -1; + } + } + + if (result != 0) { + printf("FAILED\n"); + } else { + printf("PASSED\n"); + } + + return result; +} + +int do_count_query_test__expected_output__by_algorithm_and_rate__fixed_interval(ma_resample_algorithm algorithm, ma_uint32 rateIn, ma_uint32 rateOut, ma_uint64 frameCountPerIteration) +{ + int result; + ma_resampler resampler; + + result = init_resampler(rateIn, rateOut, algorithm, &resampler); + if (result != 0) { + return 0; + } + + result = do_count_query_test__expected_output__fixed_interval(&resampler, frameCountPerIteration); + + ma_resampler_uninit(&resampler); + return result; +} + +int do_count_query_test__expected_output__by_algorithm__fixed_interval(ma_resample_algorithm algorithm, ma_uint64 frameCountPerIteration) +{ + int result; + + result = do_count_query_test__expected_output__by_algorithm_and_rate__fixed_interval(algorithm, 44100, 48000, frameCountPerIteration); + if (result != 0) { + return result; + } + + result = do_count_query_test__expected_output__by_algorithm_and_rate__fixed_interval(algorithm, 48000, 44100, frameCountPerIteration); + if (result != 0) { + return result; + } + + + result = do_count_query_test__expected_output__by_algorithm_and_rate__fixed_interval(algorithm, 44100, 192000, frameCountPerIteration); + if (result != 0) { + return result; + } + + result = do_count_query_test__expected_output__by_algorithm_and_rate__fixed_interval(algorithm, 192000, 44100, frameCountPerIteration); + if (result != 0) { + return result; + } + + return result; +} + +int do_count_query_tests__expected_output__by_algorithm(ma_resample_algorithm algorithm) +{ + int result; + + result = do_count_query_test__expected_output__by_algorithm__fixed_interval(algorithm, 1); + if (result != 0) { + return result; + } + + result = do_count_query_test__expected_output__by_algorithm__fixed_interval(algorithm, 16); + if (result != 0) { + return result; + } + + result = do_count_query_test__expected_output__by_algorithm__fixed_interval(algorithm, 127); + if (result != 0) { + return result; + } + + return 0; +} + +int do_count_query_tests__expected_output() +{ + int result; + + printf("Linear\n"); + result = do_count_query_tests__expected_output__by_algorithm(ma_resample_algorithm_linear); + if (result != 0) { + return result; + } + + printf("Speex\n"); + result = do_count_query_tests__expected_output__by_algorithm(ma_resample_algorithm_speex); + if (result != 0) { + return result; + } + + return 0; +} + + +int do_count_query_tests() +{ + int result; + + result = do_count_query_tests__expected_output(); + if (result != 0) { + return result; + } + + result = do_count_query_tests__required_input(); + if (result != 0) { + return result; + } + + return 0; +} + +int main(int argc, char** argv) +{ + int result; + + (void)argc; + (void)argv; + + result = do_count_query_tests(); + if (result != 0) { + return result; + } + + return 0; +} \ No newline at end of file diff --git a/tests/ma_test_0.vcxproj b/tests/ma_test_0.vcxproj index 5f11b389..364dc7dd 100644 --- a/tests/ma_test_0.vcxproj +++ b/tests/ma_test_0.vcxproj @@ -327,12 +327,20 @@ true - false - false - false - false - false - false + true + true + true + true + true + true + + + true + true + true + true + true + true true @@ -350,6 +358,7 @@ true true + true true @@ -416,6 +425,30 @@ + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + diff --git a/tests/ma_test_0.vcxproj.filters b/tests/ma_test_0.vcxproj.filters index 442e827b..2b87beab 100644 --- a/tests/ma_test_0.vcxproj.filters +++ b/tests/ma_test_0.vcxproj.filters @@ -13,6 +13,9 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {7a8cc5ec-bdc2-4721-b0e8-e21796ec6b9a} + @@ -72,6 +75,12 @@ Source Files + + Source Files + + + Source Files\speex_resampler + @@ -83,5 +92,14 @@ Source Files + + Source Files\speex_resampler + + + Source Files\speex_resampler + + + Source Files\speex_resampler + \ No newline at end of file