diff --git a/research/mal_resampler.h b/research/mal_resampler.h
index 9ffd38b1..a176cff9 100644
--- a/research/mal_resampler.h
+++ b/research/mal_resampler.h
@@ -51,22 +51,19 @@ Random Notes:
typedef struct mal_resampler mal_resampler;
/* Client callbacks. */
-typedef mal_uint32 (* mal_resampler_read_from_client_proc) (mal_resampler* pResampler, mal_uint32 frameCount, void** ppFrames);
+typedef mal_uint32 (* mal_resampler_read_from_client_proc)(mal_resampler* pResampler, mal_uint32 frameCount, void** ppFrames);
/* Backend functions. */
-typedef mal_result (* mal_resampler_init_proc) (mal_resampler* pResampler);
-typedef mal_uint64 (* mal_resampler_read_proc) (mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames);
-typedef mal_uint64 (* mal_resampler_seek_proc) (mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
-typedef mal_result (* mal_resampler_get_cached_time_proc) (mal_resampler* pResampler, double* pInputTime, double* pOutputTime);
-typedef mal_uint64 (* mal_resampler_get_required_input_frame_count_proc) (mal_resampler* pResampler, mal_uint64 outputFrameCount);
-typedef mal_uint64 (* mal_resampler_get_expected_output_frame_count_proc)(mal_resampler* pResampler, mal_uint64 inputFrameCount);
+typedef mal_result (* mal_resampler_init_proc)(mal_resampler* pResampler);
+typedef mal_uint64 (* mal_resampler_read_proc)(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames);
+typedef mal_uint64 (* mal_resampler_seek_proc)(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
typedef enum
{
- mal_resample_algorithm_sinc = 0, /* Default. */
- mal_resample_algorithm_linear, /* Fastest. */
- mal_resample_algorithm_passthrough /* No resampling. */
-} mal_resample_algorithm;
+ mal_resampler_algorithm_sinc = 0, /* Default. */
+ mal_resampler_algorithm_linear, /* Fastest. */
+ mal_resampler_algorithm_passthrough /* No resampling. */
+} mal_resampler_algorithm;
typedef enum
{
@@ -81,7 +78,7 @@ typedef struct
mal_uint32 sampleRateIn;
mal_uint32 sampleRateOut;
double ratio; /* ratio = in/out */
- mal_resample_algorithm algorithm;
+ mal_resampler_algorithm algorithm;
mal_resampler_end_of_input_mode endOfInputMode;
mal_resampler_read_from_client_proc onRead;
void* pUserData;
@@ -94,7 +91,7 @@ struct mal_resampler
float f32[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(float)];
mal_int16 s16[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(mal_int16)];
} cache; /* Do not use directly. Keep this as the first member of this structure for SIMD alignment purposes. */
- mal_uint16 firstCachedFrame;
+ mal_uint16 firstCachedFrameOffset;
mal_uint16 cacheLengthInFrames; /* The number of valid frames sitting in the cache. May be less than the cache's capacity. */
mal_uint16 windowLength;
double windowTime; /* By input rate. Relative to the start of the cache. */
@@ -102,9 +99,6 @@ struct mal_resampler
mal_resampler_init_proc init;
mal_resampler_read_proc read;
mal_resampler_seek_proc seek;
- mal_resampler_get_cached_time_proc getCachedTime;
- mal_resampler_get_required_input_frame_count_proc getRequiredInputFrameCount;
- mal_resampler_get_expected_output_frame_count_proc getExpectedOutputFrameCount;
};
/*
@@ -149,8 +143,6 @@ mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount,
Retrieves the number of cached input frames.
This is equivalent to: (mal_uint64)ceil(mal_resampler_get_cached_input_time(pResampler));
-
-See also: mal_resampler_get_cached_frame_counts()
*/
mal_uint64 mal_resampler_get_cached_input_frame_count(mal_resampler* pResampler);
@@ -158,20 +150,24 @@ mal_uint64 mal_resampler_get_cached_input_frame_count(mal_resampler* pResampler)
Retrieves the number of whole output frames that can be calculated from the currently cached input frames.
This is equivalent to: (mal_uint64)floor(mal_resampler_get_cached_output_time(pResampler));
-
-See also: mal_resampler_get_cached_frame_counts()
*/
mal_uint64 mal_resampler_get_cached_output_frame_count(mal_resampler* pResampler);
/*
The same as mal_resampler_get_cached_input_frame_count(), except returns a fractional value representing the exact amount
of time in input rate making up the cached input.
+
+When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames currently sitting in the
+window are not included in the calculation.
*/
double mal_resampler_get_cached_input_time(mal_resampler* pResampler);
/*
The same as mal_resampler_get_cached_output_frame_count(), except returns a fractional value representing the exact amount
of time in output rate making up the cached output.
+
+When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames currently sitting in the
+window are not included in the calculation.
*/
double mal_resampler_get_cached_output_time(mal_resampler* pResampler);
@@ -192,10 +188,10 @@ Calculates the number of whole output frames that would be output after fully re
input frames from the client.
A detail to keep in mind is how cached input frames are handled. This function calculates the output frame count based on
-inputFrameCount + mal_resampler_get_cached_input_frame_count(). It essentially calcualtes how many output frames will be
-returned if an additional inputFrameCount frames were read from the client and consumed by the resampler. You can adjust
-the return value by mal_resampler_get_cached_output_frame_count() which calculates the number of output frames that can be
-output from the currently cached input.
+inputFrameCount + mal_resampler_get_cached_input_time(). It essentially calcualtes how many output frames will be returned
+if an additional inputFrameCount frames were read from the client and consumed by the resampler. You can adjust the return
+value by mal_resampler_get_cached_output_frame_count() which calculates the number of output frames that can be output from
+the currently cached input.
When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames sitting in the filter
window are not included in the calculation.
@@ -206,25 +202,16 @@ mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampl
#ifdef MINI_AL_IMPLEMENTATION
mal_uint64 mal_resampler_read__passthrough(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames);
mal_uint64 mal_resampler_seek__passthrough(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
-mal_result mal_resampler_get_cached_time__passthrough(mal_resampler* pResampler, double* pInputTime, double* pOutputTime);
-mal_uint64 mal_resampler_get_required_input_frame_count__passthrough(mal_resampler* pResampler, mal_uint64 outputFrameCount);
-mal_uint64 mal_resampler_get_expected_output_frame_count__passthrough(mal_resampler* pResampler, mal_uint64 inputFrameCount);
mal_uint64 mal_resampler_read__linear(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames);
mal_uint64 mal_resampler_seek__linear(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
-mal_result mal_resampler_get_cached_time__linear(mal_resampler* pResampler, double* pInputTime, double* pOutputTime);
-mal_uint64 mal_resampler_get_required_input_frame_count__linear(mal_resampler* pResampler, mal_uint64 outputFrameCount);
-mal_uint64 mal_resampler_get_expected_output_frame_count__linear(mal_resampler* pResampler, mal_uint64 inputFrameCount);
mal_result mal_resampler_init__sinc(mal_resampler* pResampler);
mal_uint64 mal_resampler_read__sinc(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames);
mal_uint64 mal_resampler_seek__sinc(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
-mal_result mal_resampler_get_cached_time__sinc(mal_resampler* pResampler, double* pInputTime, double* pOutputTime);
-mal_uint64 mal_resampler_get_required_input_frame_count__sinc(mal_resampler* pResampler, mal_uint64 outputFrameCount);
-mal_uint64 mal_resampler_get_expected_output_frame_count__sinc(mal_resampler* pResampler, mal_uint64 inputFrameCount);
/* TODO: Add this to mini_al.h */
-#define MAL_ALIGN_INT(val, alignment) (((val) + ((alignment-1))) & ~((alignment)-1))
+#define MAL_ALIGN_INT(val, alignment) (((val) + ((alignment)-1)) & ~((alignment)-1))
#define MAL_ALIGN_PTR(ptr, alignment) (void*)MAL_ALIGN_INT(((mal_uintptr)(ptr)), (alignment))
/*
@@ -276,34 +263,25 @@ mal_result mal_resampler_init(const mal_resampler_config* pConfig, mal_resampler
}
switch (pResampler->config.algorithm) {
- case mal_resample_algorithm_passthrough:
+ case mal_resampler_algorithm_passthrough:
{
- pResampler->init = NULL;
- pResampler->read = mal_resampler_read__passthrough;
- pResampler->seek = mal_resampler_seek__passthrough;
- pResampler->getCachedTime = mal_resampler_get_cached_time__passthrough;
- pResampler->getRequiredInputFrameCount = mal_resampler_get_required_input_frame_count__passthrough;
- pResampler->getExpectedOutputFrameCount = mal_resampler_get_expected_output_frame_count__passthrough;
+ pResampler->init = NULL;
+ pResampler->read = mal_resampler_read__passthrough;
+ pResampler->seek = mal_resampler_seek__passthrough;
} break;
- case mal_resample_algorithm_linear:
+ case mal_resampler_algorithm_linear:
{
- pResampler->init = NULL;
- pResampler->read = mal_resampler_read__linear;
- pResampler->seek = mal_resampler_seek__linear;
- pResampler->getCachedTime = mal_resampler_get_cached_time__linear;
- pResampler->getRequiredInputFrameCount = mal_resampler_get_required_input_frame_count__linear;
- pResampler->getExpectedOutputFrameCount = mal_resampler_get_expected_output_frame_count__linear;
+ pResampler->init = NULL;
+ pResampler->read = mal_resampler_read__linear;
+ pResampler->seek = mal_resampler_seek__linear;
} break;
- case mal_resample_algorithm_sinc:
+ case mal_resampler_algorithm_sinc:
{
- pResampler->init = mal_resampler_init__sinc;
- pResampler->read = mal_resampler_read__sinc;
- pResampler->seek = mal_resampler_seek__sinc;
- pResampler->getCachedTime = mal_resampler_get_cached_time__sinc;
- pResampler->getRequiredInputFrameCount = mal_resampler_get_required_input_frame_count__sinc;
- pResampler->getExpectedOutputFrameCount = mal_resampler_get_expected_output_frame_count__sinc;
+ pResampler->init = mal_resampler_init__sinc;
+ pResampler->read = mal_resampler_read__sinc;
+ pResampler->seek = mal_resampler_seek__sinc;
} break;
}
@@ -369,6 +347,13 @@ mal_uint64 mal_resampler_read(mal_resampler* pResampler, mal_uint64 frameCount,
return mal_resampler_seek(pResampler, frameCount, 0);
}
+ /* Special case for passthrough. That has a specialized function for reading for efficiency. */
+ if (pResampler->config.algorithm == mal_resampler_algorithm_passthrough) {
+ return pResampler->read(pResampler, frameCount, ppFrames);
+ }
+
+
+
return pResampler->read(pResampler, frameCount, ppFrames);
}
@@ -382,6 +367,13 @@ mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount,
return 0; /* Nothing to do, so return early. */
}
+ /* Special case for passthrough. That has a specialized function for reading for efficiency. */
+ if (pResampler->config.algorithm == mal_resampler_algorithm_passthrough) {
+ return pResampler->seek(pResampler, frameCount, options);
+ }
+
+
+
return pResampler->seek(pResampler, frameCount, options);
}
@@ -396,43 +388,60 @@ mal_uint64 mal_resampler_get_cached_output_frame_count(mal_resampler* pResampler
return (mal_uint64)floor(mal_resampler_get_cached_output_time(pResampler));
}
+double mal_resampler__calculate_cached_input_time(mal_resampler* pResampler)
+{
+ /*
+ The cached input time depends on whether or not the end of the input is being consumed. If so, it's the difference between the
+ last cached frame and the halfway point of the window, rounded down. Otherwise it's between the last cached frame and the end
+ of the window.
+ */
+ double cachedInputTime = pResampler->cacheLengthInFrames;
+ if (pResampler->config.endOfInputMode == mal_resampler_end_of_input_mode_consume) {
+ cachedInputTime -= (pResampler->windowTime + (pResampler->windowLength >> 1));
+ } else {
+ cachedInputTime -= (pResampler->windowTime + pResampler->windowLength);
+ }
+
+ return cachedInputTime;
+}
double mal_resampler_get_cached_input_time(mal_resampler* pResampler)
{
- if (pResampler == NULL || pResampler->getCachedTime == NULL) {
+ if (pResampler == NULL) {
return 0; /* Invalid args. */
}
- double inputTime = 0;
- double outputTime = 0;
- mal_result result = pResampler->getCachedTime(pResampler, &inputTime, &outputTime);
- if (result != MAL_SUCCESS) {
+ /* Special case for passthrough. Nothing is ever cached. */
+ if (pResampler->config.algorithm == mal_resampler_algorithm_passthrough) {
return 0;
}
- return inputTime;
+ return mal_resampler__calculate_cached_input_time(pResampler);
+}
+
+double mal_resampler__calculate_cached_output_time(mal_resampler* pResampler)
+{
+ return mal_resampler__calculate_cached_input_time(pResampler) / pResampler->config.ratio;
}
double mal_resampler_get_cached_output_time(mal_resampler* pResampler)
{
- if (pResampler == NULL || pResampler->getCachedTime == NULL) {
+ if (pResampler == NULL) {
return 0; /* Invalid args. */
}
- double inputTime = 0;
- double outputTime = 0;
- mal_result result = pResampler->getCachedTime(pResampler, &inputTime, &outputTime);
- if (result != MAL_SUCCESS) {
+ /* Special case for passthrough. Nothing is ever cached. */
+ if (pResampler->config.algorithm == mal_resampler_algorithm_passthrough) {
return 0;
}
- return outputTime;
+ return mal_resampler__calculate_cached_output_time(pResampler);
}
mal_uint64 mal_resampler_get_required_input_frame_count(mal_resampler* pResampler, mal_uint64 outputFrameCount)
{
- if (pResampler == NULL || pResampler->getRequiredInputFrameCount == NULL) {
+ if (pResampler == NULL) {
return 0; /* Invalid args. */
}
@@ -440,12 +449,38 @@ mal_uint64 mal_resampler_get_required_input_frame_count(mal_resampler* pResample
return 0;
}
- return pResampler->getRequiredInputFrameCount(pResampler, outputFrameCount);
+ /* Special case for passthrough. */
+ if (pResampler->config.algorithm == mal_resampler_algorithm_passthrough) {
+ return outputFrameCount;
+ }
+
+ /* First grab the amount of output time sitting in the cache. */
+ double cachedOutputTime = mal_resampler__calculate_cached_output_time(pResampler);
+ if (cachedOutputTime >= outputFrameCount) {
+ return 0; /* All of the necessary input data is cached. No additional data is required from the client. */
+ }
+
+ /*
+ Getting here means more input data will be required. A detail to consider here is that we are accepting an unsigned 64-bit integer
+ for the output frame count, however we need to consider sub-frame timing which we're doing by using a double. There will not be
+ enough precision in the double to represent the whole 64-bit range of the input variable. For now I'm not handling this explicitly
+ because I think it's unlikely outputFrameCount will be set to something so huge anyway, but it will be something to think about in
+ order to get this working properly for the whole 64-bit range.
+
+ The return value must always be larger than 0 after this point. If it's not we have an error.
+ */
+ double nonCachedOutputTime = outputFrameCount - cachedOutputTime;
+ mal_assert(nonCachedOutputTime > 0);
+
+ mal_uint64 requiredInputFrames = (mal_uint64)ceil(nonCachedOutputTime * pResampler->config.ratio);
+ mal_assert(requiredInputFrames > 0);
+
+ return requiredInputFrames;
}
mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampler, mal_uint64 inputFrameCount)
{
- if (pResampler == NULL || pResampler->getExpectedOutputFrameCount == NULL) {
+ if (pResampler == NULL) {
return 0; /* Invalid args. */
}
@@ -453,7 +488,13 @@ mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampl
return 0;
}
- return pResampler->getExpectedOutputFrameCount(pResampler, inputFrameCount);
+ /* Special case for passthrough. */
+ if (pResampler->config.algorithm == mal_resampler_algorithm_passthrough) {
+ return inputFrameCount;
+ }
+
+ /* What we're actually calculating here is how many whole output frames will be calculated after consuming inputFrameCount + mal_resampler_get_cached_input_time(). */
+ return (mal_uint64)floor((mal_resampler__calculate_cached_input_time(pResampler) + inputFrameCount) / pResampler->config.ratio);
}
@@ -548,39 +589,6 @@ mal_uint64 mal_resampler_seek__passthrough(mal_resampler* pResampler, mal_uint64
return totalFramesRead;
}
-mal_result mal_resampler_get_cached_time__passthrough(mal_resampler* pResampler, double* pInputTime, double* pOutputTime)
-{
- mal_assert(pResampler != NULL);
- mal_assert(pInputTime != NULL);
- mal_assert(pOutputTime != NULL);
-
- /* The passthrough implementation never caches, so this is always 0. */
- *pInputTime = 0;
- *pOutputTime = 0;
-
- return MAL_SUCCESS;
-}
-
-mal_uint64 mal_resampler_get_required_input_frame_count__passthrough(mal_resampler* pResampler, mal_uint64 outputFrameCount)
-{
- mal_assert(pResampler != NULL);
- mal_assert(outputFrameCount > 0);
-
- /* For passthrough input and output is the same. */
- (void)pResampler;
- return outputFrameCount;
-}
-
-mal_uint64 mal_resampler_get_expected_output_frame_count__passthrough(mal_resampler* pResampler, mal_uint64 inputFrameCount)
-{
- mal_assert(pResampler != NULL);
- mal_assert(inputFrameCount > 0);
-
- /* For passthrough input and output is the same. */
- (void)pResampler;
- return inputFrameCount;
-}
-
/*
Linear
@@ -612,36 +620,6 @@ mal_uint64 mal_resampler_seek__linear(mal_resampler* pResampler, mal_uint64 fram
return 0;
}
-mal_result mal_resampler_get_cached_time__linear(mal_resampler* pResampler, double* pInputTime, double* pOutputTime)
-{
- mal_assert(pResampler != NULL);
- mal_assert(pInputTime != NULL);
- mal_assert(pOutputTime != NULL);
-
- /* TODO: Implement me. */
- return MAL_ERROR;
-}
-
-mal_uint64 mal_resampler_get_required_input_frame_count__linear(mal_resampler* pResampler, mal_uint64 outputFrameCount)
-{
- mal_assert(pResampler != NULL);
- mal_assert(outputFrameCount > 0);
-
- /* TODO: Implement me. */
- (void)pResampler;
- return 0;
-}
-
-mal_uint64 mal_resampler_get_expected_output_frame_count__linear(mal_resampler* pResampler, mal_uint64 inputFrameCount)
-{
- mal_assert(pResampler != NULL);
- mal_assert(inputFrameCount > 0);
-
- /* TODO: Implement me. */
- (void)pResampler;
- return 0;
-}
-
/*
Sinc
@@ -681,34 +659,4 @@ mal_uint64 mal_resampler_seek__sinc(mal_resampler* pResampler, mal_uint64 frameC
return 0;
}
-mal_result mal_resampler_get_cached_time__sinc(mal_resampler* pResampler, double* pInputTime, double* pOutputTime)
-{
- mal_assert(pResampler != NULL);
- mal_assert(pInputTime != NULL);
- mal_assert(pOutputTime != NULL);
-
- /* TODO: Implement me. */
- return MAL_ERROR;
-}
-
-mal_uint64 mal_resampler_get_required_input_frame_count__sinc(mal_resampler* pResampler, mal_uint64 outputFrameCount)
-{
- mal_assert(pResampler != NULL);
- mal_assert(outputFrameCount > 0);
-
- /* TODO: Implement me. */
- (void)pResampler;
- return 0;
-}
-
-mal_uint64 mal_resampler_get_expected_output_frame_count__sinc(mal_resampler* pResampler, mal_uint64 inputFrameCount)
-{
- mal_assert(pResampler != NULL);
- mal_assert(inputFrameCount > 0);
-
- /* TODO: Implement me. */
- (void)pResampler;
- return 0;
-}
-
#endif
diff --git a/tests/mal_test_0.vcxproj b/tests/mal_test_0.vcxproj
index 594a236c..1420d766 100644
--- a/tests/mal_test_0.vcxproj
+++ b/tests/mal_test_0.vcxproj
@@ -322,6 +322,7 @@
+
diff --git a/tests/mal_test_0.vcxproj.filters b/tests/mal_test_0.vcxproj.filters
index ac4dbbc9..8e66fa8c 100644
--- a/tests/mal_test_0.vcxproj.filters
+++ b/tests/mal_test_0.vcxproj.filters
@@ -44,5 +44,8 @@
Source Files
+
+ Source Files
+
\ No newline at end of file