- I've had a bug in the past where a single call to read() returns too many samples. It essentially computes more
samples than the input data would allow. The input data would get consumed, but output samples would continue to
get computed up to the requested frame count, filling in the end with zeroes. This is completely wrong because
the return value needs to be used to know whether or not the end of the input has been reached.
Random Notes:
- You cannot change the algorithm after initialization.
- It is recommended to keep the ma_resampler object aligned to MA_SIMD_ALIGNMENT, though it is not necessary.
- Ratios need to be in the range of MA_RESAMPLER_MIN_RATIO and MA_RESAMPLER_MAX_RATIO. This is enough to convert
to and from 8000 and 384000, which is the smallest and largest standard rates supported by miniaudio. If you need
extreme ratios then you will need to chain resamplers together.
*/
#ifndef ma_resampler_h
#define ma_resampler_h
#define MA_RESAMPLER_SEEK_NO_CLIENT_READ (1 << 0) /* When set, does not read anything from the client when seeking. This does _not_ call onRead(). */
#define MA_RESAMPLER_SEEK_INPUT_RATE (1 << 1) /* When set, treats the specified frame count based on the input sample rate rather than the output sample rate. */
ma_resampler_end_of_input_mode_consume = 0, /* When the end of the input stream is reached, consume the last input PCM frames (do not leave them in the internal cache). Default. */
ma_resampler_end_of_input_mode_no_consume /* When the end of the input stream is reached, do _not_ consume the last input PCM frames (leave them in the internal cache). Use this in streaming situations. */
} ma_resampler_end_of_input_mode;
enum ma_resampler_seek_mode
{
ma_resampler_seek_mode_none = 0, /* No seeking (normal read). */
ma_resampler_seek_mode_output, /* Seek by output rate. */
ma_resampler_seek_mode_input /* Seek by input rate. */
};
typedef struct
{
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRateIn;
ma_uint32 sampleRateOut;
double ratio; /* ratio = in/out */
ma_resampler_algorithm algorithm;
ma_resampler_end_of_input_mode endOfInputMode;
ma_stream_layout layout; /* Interleaved or deinterleaved. */
} window; /* Keep this as the first member of this structure for SIMD alignment purposes. */
/*ma_uint32 cacheStrideInFrames;*/ /* The number of the samples between channels in the cache. The first sample for channel 0 is cacheStrideInFrames*0. The first sample for channel 1 is cacheStrideInFrames*1, etc. */
/*ma_uint16 cacheLengthInFrames;*/ /* The number of valid frames sitting in the cache, including the filter window. May be less than the cache's capacity. */
ma_uint16 windowLength;
double windowTime; /* By input rate. Relative to the start of the cache. */
if (targetInputFrameCount > inputBufferSizeInFrames) {
targetInputFrameCount = inputBufferSizeInFrames;
}
inputFrameCount = onRead(pResampler, targetInputFrameCount, &ppInputFrames[0]); /* Don't check if inputFrameCount is 0 and break from the loop. May want to extract the last bit that's sitting in the window. */
result = ma_resampler_process_pcm_frames(pResampler, &outputFrameCount, &ppRunningFramesOut[0], &inputFrameCount, &ppInputFrames[0]);
if (result != MA_SUCCESS) {
break;
}
outputFramesRemaining -= outputFrameCount;
if (outputFramesRemaining == 0) {
break;
}
if (inputFrameCount < targetInputFrameCount) {
break; /* Input data has been exhausted. */
}
if (pResampler->config.layout == ma_stream_layout_interleaved) {
/* We need to read from the client which means we need to loop. */
/*while (totalFramesSeeked < frameCount) {
}*/
}
} else {
/* Seeking by output rate. */
if ((options & MA_RESAMPLER_SEEK_NO_CLIENT_READ) != 0) {
/* Not reading from the client. This is a fast-ish path, though I'm not doing this in constant time like when seeking by input rate. It's easier to just loop. */
} else {
/* Reading from the client. This case is basically the same as reading, but without the filtering. */
/* What we're actually calculating here is how many whole output frames will be calculated after consuming inputFrameCount + ma_resampler_get_cached_input_time(). */
/* TODO: Implement an s16 optimized implementation. */
/* I'm cheating here and just using the f32 implementation and converting to s16. This will be changed later - for now just focusing on getting it working. */
/* TODO: Implement an s16 optimized implementation. */
/* I'm cheating here and just using the f32 implementation and converting to s16. This will be changed later - for now just focusing on getting it working. */