Commit 7d377894 authored by David Reid's avatar David Reid

Resampling and LPF work.

parent df2903a0
...@@ -5,21 +5,45 @@ ...@@ -5,21 +5,45 @@
TODO: TODO:
- Document passthrough behaviour of the biquad filter and how it doesn't update previous inputs and outputs. - Document passthrough behaviour of the biquad filter and how it doesn't update previous inputs and outputs.
- Document how changing biquad constants requires reinitialization of the filter (due to issue above). - Document how changing biquad constants requires reinitialization of the filter (due to issue above).
- Document how ma_biquad_process() and ma_lpf_process() supports in-place filtering by passing in the same buffer for both the input and output.
*/ */
typedef struct typedef struct
{ {
ma_format format; ma_format format;
ma_uint32 channels; ma_uint32 channels;
float a0; double a0;
float a1; double a1;
float a2; double a2;
float b0; double b0;
float b1; double b1;
float b2; double b2;
#if 0
union
{
struct
{
double a0;
double a1;
double a2;
double b0;
double b1;
double b2;
} f32;
struct
{
ma_int32 a0;
ma_int32 a1;
ma_int32 a2;
ma_int32 b0;
ma_int32 b1;
ma_int32 b2;
} s16;
} constants;
#endif
} ma_biquad_config; } ma_biquad_config;
ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, float a0, float a1, float a2, float b0, float b1, float b2); ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double a0, double a1, double a2, double b0, double b1, double b2);
typedef struct typedef struct
{ {
...@@ -62,7 +86,7 @@ ma_result ma_lpf_process(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ...@@ -62,7 +86,7 @@ ma_result ma_lpf_process(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn,
#if defined(MINIAUDIO_IMPLEMENTATION) #if defined(MINIAUDIO_IMPLEMENTATION)
ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, float a0, float a1, float a2, float b0, float b1, float b2) ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double a0, double a1, double a2, double b0, double b1, double b2)
{ {
ma_biquad_config config; ma_biquad_config config;
...@@ -121,11 +145,11 @@ ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFrame ...@@ -121,11 +145,11 @@ ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFrame
{ {
ma_uint32 n; ma_uint32 n;
ma_uint32 c; ma_uint32 c;
float a1 = pBQ->config.a1; double a1 = pBQ->config.a1;
float a2 = pBQ->config.a2; double a2 = pBQ->config.a2;
float b0 = pBQ->config.b0; double b0 = pBQ->config.b0;
float b1 = pBQ->config.b1; double b1 = pBQ->config.b1;
float b2 = pBQ->config.b2; double b2 = pBQ->config.b2;
if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
...@@ -149,18 +173,18 @@ ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFrame ...@@ -149,18 +173,18 @@ ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFrame
for (n = 0; n < frameCount; n += 1) { for (n = 0; n < frameCount; n += 1) {
for (c = 0; c < pBQ->config.channels; c += 1) { for (c = 0; c < pBQ->config.channels; c += 1) {
float x2 = pBQ->x2[c]; double x2 = pBQ->x2[c];
float x1 = pBQ->x1[c]; double x1 = pBQ->x1[c];
float x0 = pX[n*pBQ->config.channels + c]; double x0 = pX[n*pBQ->config.channels + c];
float y2 = pBQ->y2[c]; double y2 = pBQ->y2[c];
float y1 = pBQ->y1[c]; double y1 = pBQ->y1[c];
float y0 = b0*x0 + b1*x1 + b2*x2 - a1*y1 - a2*y2; double y0 = b0*x0 + b1*x1 + b2*x2 - a1*y1 - a2*y2;
pY[n*pBQ->config.channels + c] = y0; pY[n*pBQ->config.channels + c] = (float)y0;
pBQ->x2[c] = x1; pBQ->x2[c] = (float)x1;
pBQ->x1[c] = x0; pBQ->x1[c] = (float)x0;
pBQ->y2[c] = y1; pBQ->y2[c] = (float)y1;
pBQ->y1[c] = y0; pBQ->y1[c] = (float)y0;
} }
} }
} else { } else {
...@@ -212,12 +236,12 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF) ...@@ -212,12 +236,12 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
c = cos(w); c = cos(w);
a = s / (2*q); a = s / (2*q);
bqConfig.a0 = (float)( 1 + a); bqConfig.a0 = (double)( 1 + a);
bqConfig.a1 = (float)(-2 * c); bqConfig.a1 = (double)(-2 * c);
bqConfig.a2 = (float)( 1 - a); bqConfig.a2 = (double)( 1 - a);
bqConfig.b0 = (float)((1 - c) / 2); bqConfig.b0 = (double)((1 - c) / 2);
bqConfig.b1 = (float)( 1 - c); bqConfig.b1 = (double)( 1 - c);
bqConfig.b2 = (float)((1 - c) / 2); bqConfig.b2 = (double)((1 - c) / 2);
bqConfig.format = pConfig->format; bqConfig.format = pConfig->format;
bqConfig.channels = pConfig->channels; bqConfig.channels = pConfig->channels;
......
...@@ -203,23 +203,25 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, ma ...@@ -203,23 +203,25 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, ma
{ {
ma_uint64 frameCountOut; ma_uint64 frameCountOut;
ma_uint64 frameCountIn; ma_uint64 frameCountIn;
ma_uint64 iFrameOut;
ma_uint64 iFrameIn;
ma_uint64 iChannel;
float ratioInOut; float ratioInOut;
MA_ASSERT(pResampler != NULL); MA_ASSERT(pResampler != NULL);
MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFrameCountOut != NULL); MA_ASSERT(pFrameCountOut != NULL);
MA_ASSERT(pFramesIn != NULL);
MA_ASSERT(pFrameCountIn != NULL); MA_ASSERT(pFrameCountIn != NULL);
frameCountOut = *pFrameCountOut; frameCountOut = *pFrameCountOut;
frameCountIn = *pFrameCountIn; frameCountIn = *pFrameCountIn;
ratioInOut = (float)pResampler->config.sampleRateIn / (float)pResampler->config.sampleRateOut; if (frameCountOut == 0 || frameCountIn == 0) {
return MA_INVALID_ARGS; /* Nothing to do. */
}
if (pFramesIn != NULL) { ratioInOut = (float)pResampler->config.sampleRateIn / (float)pResampler->config.sampleRateOut;
/* Pass in data from the input buffer. */
ma_uint64 iFrameOut;
ma_uint64 iFrameIn;
ma_uint64 iChannel;
iFrameOut = 0; iFrameOut = 0;
iFrameIn = 0; iFrameIn = 0;
...@@ -244,49 +246,63 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, ma ...@@ -244,49 +246,63 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, ma
} }
for (;;) { for (;;) {
float t0;
float t1;
float y;
if (iFrameOut >= frameCountOut || iFrameIn >= frameCountIn) { if (iFrameOut >= frameCountOut || iFrameIn >= frameCountIn) {
break; break;
} }
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { /* We can't interpolate if our interpolation factor (time relative to x0) is greater than 1. */
y = ma_mix_f32_fast(pResampler->state.linear.x0[iChannel], pResampler->state.linear.x1[iChannel], pResampler->state.linear.t); if (pResampler->state.linear.t > 1) {
}
t0 = pResampler->state.linear.t;
t1 = t0 + ratioInOut;
if (t1 >= 1) {
/* Need to load the next input frame. */ /* Need to load the next input frame. */
iFrameIn += (ma_uint64)t1; iFrameIn += (ma_uint64)pResampler->state.linear.t;
if (iFrameIn > 0) { if (iFrameIn < frameCountIn) {
/* We have enough input frames remaining to bring the time down to 0..1. */
MA_ASSERT(iFrameIn > 0);
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
pResampler->state.linear.x0[iChannel] = pX[(iFrameIn-1)*pResampler->config.channels + iChannel]; pResampler->state.linear.x0[iChannel] = pX[(iFrameIn-1)*pResampler->config.channels + iChannel];
pResampler->state.linear.x1[iChannel] = pX[(iFrameIn-0)*pResampler->config.channels + iChannel]; pResampler->state.linear.x1[iChannel] = pX[(iFrameIn-0)*pResampler->config.channels + iChannel];
} }
/* The time should always be relative to x0, and should not be greater than 1. */
pResampler->state.linear.t -= floorf(pResampler->state.linear.t);
MA_ASSERT(pResampler->state.linear.t >= 0 && pResampler->state.linear.t <= 1);
} else {
/* Ran out of input frames. Make sure we consume the rest of the input frames by adjusting our input time appropriately. */
if (frameCountIn > 1) {
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
pResampler->state.linear.x0[iChannel] = pX[(frameCountIn-2)*pResampler->config.channels + iChannel];
pResampler->state.linear.x1[iChannel] = pX[(frameCountIn-1)*pResampler->config.channels + iChannel];
}
} else { } else {
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
pResampler->state.linear.x0[iChannel] = pResampler->state.linear.x1[iChannel]; pResampler->state.linear.x0[iChannel] = pResampler->state.linear.x1[iChannel];
pResampler->state.linear.x1[iChannel] = pX[iFrameIn*pResampler->config.channels + iChannel]; pResampler->state.linear.x1[iChannel] = pX[(frameCountIn-1)*pResampler->config.channels + iChannel];
} }
} }
pResampler->state.linear.t -= (iFrameIn - frameCountIn) + 1;
iFrameIn = frameCountIn;
break;
}
} }
pResampler->state.linear.t = t1 - floorf(t1); /* The time should always be relative to x0, and should not be greater than 1. */ for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
pY[iFrameOut*pResampler->config.channels + iChannel] = ma_mix_f32_fast(pResampler->state.linear.x0[iChannel], pResampler->state.linear.x1[iChannel], pResampler->state.linear.t);
}
/* Move time forward. */
pResampler->state.linear.t += ratioInOut;
iFrameOut += 1; iFrameOut += 1;
} }
/* Here is where we set the number of frames that were consumed. */
*pFrameCountOut = iFrameOut;
*pFrameCountIn = iFrameIn;
} else { } else {
/* Format not supported. */ /* Format not supported. */
return MA_INVALID_OPERATION; return MA_INVALID_OPERATION;
} }
} else {
/* Pass in zeroes. */
return MA_INVALID_OPERATION;
}
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -299,6 +315,7 @@ static ma_result ma_resampler_process__read__linear_lpf(ma_resampler* pResampler ...@@ -299,6 +315,7 @@ static ma_result ma_resampler_process__read__linear_lpf(ma_resampler* pResampler
MA_ASSERT(pResampler != NULL); MA_ASSERT(pResampler != NULL);
MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFrameCountOut != NULL); MA_ASSERT(pFrameCountOut != NULL);
MA_ASSERT(pFramesIn != NULL);
MA_ASSERT(pFrameCountIn != NULL); MA_ASSERT(pFrameCountIn != NULL);
result = ma_resampler_process__read__linear(pResampler, pFrameCountOut, pFramesOut, pFrameCountIn, pFramesIn); result = ma_resampler_process__read__linear(pResampler, pFrameCountOut, pFramesOut, pFrameCountIn, pFramesIn);
...@@ -319,11 +336,16 @@ static ma_result ma_resampler_process__read(ma_resampler* pResampler, ma_uint64* ...@@ -319,11 +336,16 @@ static ma_result ma_resampler_process__read(ma_resampler* pResampler, ma_uint64*
MA_ASSERT(pResampler != NULL); MA_ASSERT(pResampler != NULL);
MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesOut != NULL);
/* ppFramesOut is not NULL, which means we must have a capacity. */ /* pFramesOut is not NULL, which means we must have a capacity. */
if (pFrameCountOut == NULL) { if (pFrameCountOut == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
/* It doesn't make sense to not have any input frames to process. */
if (pFrameCountIn == NULL || pFramesIn == NULL) {
return MA_INVALID_ARGS;
}
switch (pResampler->config.algorithm) switch (pResampler->config.algorithm)
{ {
case ma_resample_algorithm_linear: case ma_resample_algorithm_linear:
......
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