Commit 3e825396 authored by David Reid's avatar David Reid

Random resampler work.

parent f6561013
...@@ -52,36 +52,54 @@ Random Notes: ...@@ -52,36 +52,54 @@ Random Notes:
#ifndef ma_resampler_h #ifndef ma_resampler_h
#define 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_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. */ #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. */
#ifndef MA_RESAMPLER_CACHE_SIZE_IN_BYTES #ifndef MA_RESAMPLER_CACHE_SIZE_IN_BYTES
#define MA_RESAMPLER_CACHE_SIZE_IN_BYTES 4096 #define MA_RESAMPLER_CACHE_SIZE_IN_BYTES 4096
#endif
#ifndef MA_RESAMPLER_MAX_WINDOW_WIDTH_IN_FRAMES
#define MA_RESAMPLER_MAX_WINDOW_WIDTH_IN_FRAMES 32
#endif
#ifndef MA_RESAMPLER_MAX_WINDOW_WIDTH_IN_BYTES
#define MA_RESAMPLER_MAX_WINDOW_WIDTH_IN_BYTES (4*MA_MAX_CHANNELS*MA_RESAMPLER_MAX_WINDOW_WIDTH_IN_FRAMES)
#endif #endif
typedef struct ma_resampler ma_resampler; typedef struct ma_resampler ma_resampler;
typedef enum ma_resampler_seek_mode ma_resampler_seek_mode;
/* Client callbacks. */ /* Client callbacks. */
typedef ma_uint32 (* ma_resampler_read_from_client_proc)(ma_resampler* pResampler, ma_uint32 frameCount, void** ppFrames); typedef ma_uint32 (* ma_resampler_read_from_client_proc)(ma_resampler* pResampler, ma_uint32 frameCount, void** ppFrames);
/* Backend functions. */ /* Backend functions. */
typedef ma_result (* ma_resampler_init_proc) (ma_resampler* pResampler); typedef ma_result (* ma_resampler_init_proc) (ma_resampler* pResampler);
typedef ma_result (* ma_resampler_process_proc) (ma_resampler* pResampler, ma_uint64* pFrameCountOut, void** ppFramesOut, ma_uint64* pFrameCountIn, void** ppFramesIn, ma_resampler_seek_mode seekMode);
typedef ma_uint64 (* ma_resampler_read_f32_proc)(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames); typedef ma_uint64 (* ma_resampler_read_f32_proc)(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames);
typedef ma_uint64 (* ma_resampler_read_s16_proc)(ma_resampler* pResampler, ma_uint64 frameCount, ma_int16** ppFrames); typedef ma_uint64 (* ma_resampler_read_s16_proc)(ma_resampler* pResampler, ma_uint64 frameCount, ma_int16** ppFrames);
typedef ma_uint64 (* ma_resampler_seek_proc) (ma_resampler* pResampler, ma_uint64 frameCount, ma_uint32 options); typedef ma_uint64 (* ma_resampler_seek_proc) (ma_resampler* pResampler, ma_uint64 frameCount, ma_uint32 options);
typedef enum typedef enum
{ {
ma_resampler_algorithm_sinc = 0, /* Default. */ ma_resampler_algorithm_linear = 0, /* Default. Fastest. */
ma_resampler_algorithm_linear, /* Fastest. */ ma_resampler_algorithm_sinc /* Slower. */
} ma_resampler_algorithm; } ma_resampler_algorithm;
typedef enum typedef enum
{ {
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_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_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; } 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 typedef struct
{ {
ma_format format; ma_format format;
...@@ -100,15 +118,16 @@ struct ma_resampler ...@@ -100,15 +118,16 @@ struct ma_resampler
{ {
union union
{ {
float f32[MA_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(float)]; float f32[MA_RESAMPLER_MAX_WINDOW_WIDTH_IN_BYTES/sizeof(float)];
ma_int16 s16[MA_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(ma_int16)]; ma_int16 s16[MA_RESAMPLER_MAX_WINDOW_WIDTH_IN_BYTES/sizeof(ma_int16)];
} cache; /* Keep this as the first member of this structure for SIMD alignment purposes. */ } 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_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 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; ma_uint16 windowLength;
double windowTime; /* By input rate. Relative to the start of the cache. */ double windowTime; /* By input rate. Relative to the start of the cache. */
ma_resampler_config config; ma_resampler_config config;
ma_resampler_init_proc init; ma_resampler_init_proc init;
ma_resampler_process_proc process;
ma_resampler_read_f32_proc readF32; ma_resampler_read_f32_proc readF32;
ma_resampler_read_s16_proc readS16; ma_resampler_read_s16_proc readS16;
ma_resampler_seek_proc seek; ma_resampler_seek_proc seek;
...@@ -149,7 +168,7 @@ you should provide for a given number of output frames. ...@@ -149,7 +168,7 @@ you should provide for a given number of output frames.
You can pass NULL to [ppFramesOut], in which case a seek will be performed. When [pFrameCountOut] is not NULL, it will seek past that You can pass NULL to [ppFramesOut], in which case a seek will be performed. When [pFrameCountOut] is not NULL, it will seek past that
number of output frames. Otherwise, if [pFrameCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number number of output frames. Otherwise, if [pFrameCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number
of input frames. When seeking, [ppFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but not input of input frames. When seeking, [ppFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input
will be processed. will be processed.
It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
...@@ -163,8 +182,8 @@ Reads a number of PCM frames from the resampler. ...@@ -163,8 +182,8 @@ Reads a number of PCM frames from the resampler.
Passing in NULL for ppFrames is equivalent to calling ma_resampler_seek(pResampler, frameCount, 0). Passing in NULL for ppFrames is equivalent to calling ma_resampler_seek(pResampler, frameCount, 0).
*/ */
ma_uint64 ma_resampler_read(ma_resampler* pResampler, ma_uint64 frameCount, void** ppFrames); /*ma_uint64 ma_resampler_read(ma_resampler* pResampler, ma_uint64 frameCount, void** ppFrames);
ma_uint64 ma_resampler_read_callbacks(ma_resampler* pResampler, ma_uint64 frameCount, void** pFrames, ma_resampler_read_from_client_proc onRead, void* pUserData); ma_uint64 ma_resampler_read_callbacks(ma_resampler* pResampler, ma_uint64 frameCount, void** pFrames, ma_resampler_read_from_client_proc onRead, void* pUserData);*/
/* /*
Seeks forward by the specified number of PCM frames. Seeks forward by the specified number of PCM frames.
...@@ -175,7 +194,7 @@ Seeks forward by the specified number of PCM frames. ...@@ -175,7 +194,7 @@ Seeks forward by the specified number of PCM frames.
MA_RESAMPLER_SEEK_INPUT_RATE MA_RESAMPLER_SEEK_INPUT_RATE
Treats "frameCount" as input samples instead of output samples. Treats "frameCount" as input samples instead of output samples.
*/ */
ma_uint64 ma_resampler_seek(ma_resampler* pResampler, ma_uint64 frameCount, ma_uint32 options); /*ma_uint64 ma_resampler_seek(ma_resampler* pResampler, ma_uint64 frameCount, ma_uint32 options);*/
/* /*
Retrieves the number of cached input frames. Retrieves the number of cached input frames.
...@@ -253,11 +272,13 @@ ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler, ...@@ -253,11 +272,13 @@ ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler,
#endif #endif
ma_result ma_resampler_init__linear(ma_resampler* pResampler); ma_result ma_resampler_init__linear(ma_resampler* pResampler);
ma_result ma_resampler_process__linear(ma_resampler* pResampler, ma_uint64* pFrameCountOut, void** ppFramesOut, ma_uint64* pFrameCountIn, void** ppFramesIn, ma_resampler_seek_mode seekMode);
ma_uint64 ma_resampler_read_f32__linear(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames); ma_uint64 ma_resampler_read_f32__linear(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames);
ma_uint64 ma_resampler_read_s16__linear(ma_resampler* pResampler, ma_uint64 frameCount, ma_int16** ppFrames); ma_uint64 ma_resampler_read_s16__linear(ma_resampler* pResampler, ma_uint64 frameCount, ma_int16** ppFrames);
ma_uint64 ma_resampler_seek__linear(ma_resampler* pResampler, ma_uint64 frameCount, ma_uint32 options); ma_uint64 ma_resampler_seek__linear(ma_resampler* pResampler, ma_uint64 frameCount, ma_uint32 options);
ma_result ma_resampler_init__sinc(ma_resampler* pResampler); ma_result ma_resampler_init__sinc(ma_resampler* pResampler);
ma_result ma_resampler_process__sinc(ma_resampler* pResampler, ma_uint64* pFrameCountOut, void** ppFramesOut, ma_uint64* pFrameCountIn, void** ppFramesIn, ma_resampler_seek_mode seekMode);
ma_uint64 ma_resampler_read_f32__sinc(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames); ma_uint64 ma_resampler_read_f32__sinc(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames);
ma_uint64 ma_resampler_read_s16__sinc(ma_resampler* pResampler, ma_uint64 frameCount, ma_int16** ppFrames); ma_uint64 ma_resampler_read_s16__sinc(ma_resampler* pResampler, ma_uint64 frameCount, ma_int16** ppFrames);
ma_uint64 ma_resampler_seek__sinc(ma_resampler* pResampler, ma_uint64 frameCount, ma_uint32 options); ma_uint64 ma_resampler_seek__sinc(ma_resampler* pResampler, ma_uint64 frameCount, ma_uint32 options);
...@@ -510,6 +531,7 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR ...@@ -510,6 +531,7 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR
case ma_resampler_algorithm_linear: case ma_resampler_algorithm_linear:
{ {
pResampler->init = ma_resampler_init__linear; pResampler->init = ma_resampler_init__linear;
pResampler->process = ma_resampler_process__linear;
pResampler->readF32 = ma_resampler_read_f32__linear; pResampler->readF32 = ma_resampler_read_f32__linear;
pResampler->readS16 = ma_resampler_read_s16__linear; pResampler->readS16 = ma_resampler_read_s16__linear;
pResampler->seek = ma_resampler_seek__linear; pResampler->seek = ma_resampler_seek__linear;
...@@ -518,6 +540,7 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR ...@@ -518,6 +540,7 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR
case ma_resampler_algorithm_sinc: case ma_resampler_algorithm_sinc:
{ {
pResampler->init = ma_resampler_init__sinc; pResampler->init = ma_resampler_init__sinc;
pResampler->process = ma_resampler_process__sinc;
pResampler->readF32 = ma_resampler_read_f32__sinc; pResampler->readF32 = ma_resampler_read_f32__sinc;
pResampler->readS16 = ma_resampler_read_s16__sinc; pResampler->readS16 = ma_resampler_read_s16__sinc;
pResampler->seek = ma_resampler_seek__sinc; pResampler->seek = ma_resampler_seek__sinc;
...@@ -593,6 +616,8 @@ ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, double ratio) ...@@ -593,6 +616,8 @@ ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, double ratio)
ma_result ma_resampler_process(ma_resampler* pResampler, ma_uint64* pFrameCountOut, void** ppFramesOut, ma_uint64* pFrameCountIn, void** ppFramesIn) ma_result ma_resampler_process(ma_resampler* pResampler, ma_uint64* pFrameCountOut, void** ppFramesOut, ma_uint64* pFrameCountIn, void** ppFramesIn)
{ {
ma_resampler_seek_mode seekMode;
if (pResampler == NULL) { if (pResampler == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
...@@ -606,23 +631,23 @@ ma_result ma_resampler_process(ma_resampler* pResampler, ma_uint64* pFrameCountO ...@@ -606,23 +631,23 @@ ma_result ma_resampler_process(ma_resampler* pResampler, ma_uint64* pFrameCountO
return MA_INVALID_ARGS; /* Cannot process without any input data. */ return MA_INVALID_ARGS; /* Cannot process without any input data. */
} }
seekMode = ma_resampler_seek_mode_none;
} else { } else {
/* Seeking. */ /* Seeking. */
if (pFrameCountOut != NULL) { if (pFrameCountOut != NULL) {
/* Seeking by output frames. */ /* Seeking by output frames. */
seekMode = ma_resampler_seek_mode_output;
} else { } else {
/* Seeking by input frames. */ /* Seeking by input frames. */
if (pFrameCountIn == NULL) { if (pFrameCountIn == NULL) {
return MA_INVALID_ARGS; /* Don't have any input frames to process. */ return MA_INVALID_ARGS; /* Don't have any input frames to process. */
} }
seekMode = ma_resampler_seek_mode_input;
} }
} }
return MA_SUCCESS; return pResampler->process(pResampler, pFrameCountOut, ppFramesOut, pFrameCountIn, ppFramesIn, seekMode);
} }
ma_result ma_resampler_process_callback(ma_resampler* pResampler, ma_uint64* pFrameCountOut, void** ppFramesOut, ma_resampler_read_from_client_proc onRead, void* pUserData) ma_result ma_resampler_process_callback(ma_resampler* pResampler, ma_uint64* pFrameCountOut, void** ppFramesOut, ma_resampler_read_from_client_proc onRead, void* pUserData)
...@@ -631,9 +656,12 @@ ma_result ma_resampler_process_callback(ma_resampler* pResampler, ma_uint64* pFr ...@@ -631,9 +656,12 @@ ma_result ma_resampler_process_callback(ma_resampler* pResampler, ma_uint64* pFr
{ {
float f32[1024]; float f32[1024];
ma_int16 s16[2048]; ma_int16 s16[2048];
} buffer; } inputBuffer;
ma_uint64 bufferSizeInFrames; ma_uint64 inputBufferSizeInFrames;
ma_uint64 outputFramesRemaining; ma_uint64 outputFramesRemaining;
ma_result result;
void* ppRunningFramesOut[MA_MAX_CHANNELS];
void* ppInputFrames[MA_MAX_CHANNELS];
if (onRead == NULL) { if (onRead == NULL) {
return MA_INVALID_ARGS; /* Does not make sense to call this API without a callback... */ return MA_INVALID_ARGS; /* Does not make sense to call this API without a callback... */
...@@ -643,26 +671,70 @@ ma_result ma_resampler_process_callback(ma_resampler* pResampler, ma_uint64* pFr ...@@ -643,26 +671,70 @@ ma_result ma_resampler_process_callback(ma_resampler* pResampler, ma_uint64* pFr
if (pFrameCountOut == NULL) { if (pFrameCountOut == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
bufferSizeInFrames = sizeof(buffer)/ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels); result = MA_SUCCESS;
inputBufferSizeInFrames = sizeof(inputBuffer)/ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels);
outputFramesRemaining = *pFrameCountOut; outputFramesRemaining = *pFrameCountOut;
if (pResampler->config.layout == ma_stream_layout_interleaved) {
ppRunningFramesOut[0] = ppFramesOut[0];
ppInputFrames[0] = (void*)&inputBuffer.f32[0];
} else {
ma_uint32 iChannel;
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
ppRunningFramesOut[iChannel] = ppFramesOut[iChannel];
}
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
ppInputFrames[iChannel] = ma_offset_ptr(&inputBuffer.f32[0], inputBufferSizeInFrames * iChannel);
}
}
/* Keep reading until every output frame has been processed. */ /* Keep reading until every output frame has been processed. */
for (;;) { for (;;) {
ma_uint64 inputFrameCount = ma_resampler_get_required_input_frame_count(pResampler, outputFramesRemaining); ma_uint64 outputFrameCount;
if (inputFrameCount > bufferSizeInFrames) { ma_uint64 inputFrameCount;
inputFrameCount = bufferSizeInFrames; ma_uint64 targetInputFrameCount;
targetInputFrameCount = ma_resampler_get_required_input_frame_count(pResampler, outputFrameCount);
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(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) {
ppRunningFramesOut[0] = ma_offset_ptr(ppRunningFramesOut[0], outputFrameCount * ma_get_bytes_per_sample(pResampler->config.format));
} else {
ma_uint32 iChannel;
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
ppRunningFramesOut[iChannel] = ma_offset_ptr(ppRunningFramesOut[iChannel], outputFrameCount * ma_get_bytes_per_sample(pResampler->config.format));
}
}
} }
return MA_SUCCESS; *pFrameCountOut = *pFrameCountOut - outputFramesRemaining;
}
return result;
}
#if 0
ma_uint64 ma_resampler_read(ma_resampler* pResampler, ma_uint64 frameCount, void** ppFrames) ma_uint64 ma_resampler_read(ma_resampler* pResampler, ma_uint64 frameCount, void** ppFrames)
{ {
ma_result result; ma_result result;
...@@ -887,7 +959,7 @@ ma_uint64 ma_resampler_seek(ma_resampler* pResampler, ma_uint64 frameCount, ma_u ...@@ -887,7 +959,7 @@ ma_uint64 ma_resampler_seek(ma_resampler* pResampler, ma_uint64 frameCount, ma_u
return totalFramesSeeked; return totalFramesSeeked;
} }
#endif
ma_uint64 ma_resampler_get_cached_input_frame_count(ma_resampler* pResampler) ma_uint64 ma_resampler_get_cached_input_frame_count(ma_resampler* pResampler)
{ {
...@@ -981,6 +1053,33 @@ ma_result ma_resampler_init__linear(ma_resampler* pResampler) ...@@ -981,6 +1053,33 @@ ma_result ma_resampler_init__linear(ma_resampler* pResampler)
return MA_SUCCESS; return MA_SUCCESS;
} }
ma_result ma_resampler_process__linear(ma_resampler* pResampler, ma_uint64* pFrameCountOut, void** ppFramesOut, ma_uint64* pFrameCountIn, void** ppFramesIn, ma_resampler_seek_mode seekMode)
{
ma_assert(pResampler != NULL);
if (seekMode == ma_resampler_seek_mode_none) {
/* Standard processing. */
ma_assert(pFrameCountOut != NULL);
ma_assert(ppFramesOut != NULL);
ma_assert(pFrameCountIn != NULL);
ma_assert(ppFramesIn != NULL);
} else if (seekMode == ma_resampler_seek_mode_output) {
/* Seek by output rate. */
ma_assert(pFrameCountOut != NULL);
/* TODO: Implement me. */
} else if (seekMode == ma_resampler_seek_mode_input) {
/* Seek by input rate. */
ma_assert(pFrameCountIn != NULL);
/* TODO: Implement me. */
}
return MA_INVALID_OPERATION; /* Not yet implemented. */
}
ma_uint64 ma_resampler_read_f32__linear(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames) ma_uint64 ma_resampler_read_f32__linear(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames)
{ {
ma_assert(pResampler != NULL); ma_assert(pResampler != NULL);
...@@ -1045,6 +1144,13 @@ ma_result ma_resampler_init__sinc(ma_resampler* pResampler) ...@@ -1045,6 +1144,13 @@ ma_result ma_resampler_init__sinc(ma_resampler* pResampler)
return MA_SUCCESS; return MA_SUCCESS;
} }
ma_result ma_resampler_process__sinc(ma_resampler* pResampler, ma_uint64* pFrameCountOut, void** ppFramesOut, ma_uint64* pFrameCountIn, void** ppFramesIn, ma_resampler_seek_mode seekMode)
{
ma_assert(pResampler != NULL);
return MA_INVALID_OPERATION; /* Not yet implemented. */
}
ma_uint64 ma_resampler_read_f32__sinc(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames) ma_uint64 ma_resampler_read_f32__sinc(ma_resampler* pResampler, ma_uint64 frameCount, float** ppFrames)
{ {
ma_assert(pResampler != NULL); ma_assert(pResampler != NULL);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment