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