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

Resampling and LPF work.

parent df2903a0
......@@ -5,21 +5,45 @@
TODO:
- 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 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
{
ma_format format;
ma_uint32 channels;
float a0;
float a1;
float a2;
float b0;
float b1;
float b2;
double a0;
double a1;
double a2;
double b0;
double b1;
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_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
{
......@@ -62,7 +86,7 @@ ma_result ma_lpf_process(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn,
#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;
......@@ -121,11 +145,11 @@ ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFrame
{
ma_uint32 n;
ma_uint32 c;
float a1 = pBQ->config.a1;
float a2 = pBQ->config.a2;
float b0 = pBQ->config.b0;
float b1 = pBQ->config.b1;
float b2 = pBQ->config.b2;
double a1 = pBQ->config.a1;
double a2 = pBQ->config.a2;
double b0 = pBQ->config.b0;
double b1 = pBQ->config.b1;
double b2 = pBQ->config.b2;
if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
return MA_INVALID_ARGS;
......@@ -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 (c = 0; c < pBQ->config.channels; c += 1) {
float x2 = pBQ->x2[c];
float x1 = pBQ->x1[c];
float x0 = pX[n*pBQ->config.channels + c];
float y2 = pBQ->y2[c];
float y1 = pBQ->y1[c];
float y0 = b0*x0 + b1*x1 + b2*x2 - a1*y1 - a2*y2;
pY[n*pBQ->config.channels + c] = y0;
pBQ->x2[c] = x1;
pBQ->x1[c] = x0;
pBQ->y2[c] = y1;
pBQ->y1[c] = y0;
double x2 = pBQ->x2[c];
double x1 = pBQ->x1[c];
double x0 = pX[n*pBQ->config.channels + c];
double y2 = pBQ->y2[c];
double y1 = pBQ->y1[c];
double y0 = b0*x0 + b1*x1 + b2*x2 - a1*y1 - a2*y2;
pY[n*pBQ->config.channels + c] = (float)y0;
pBQ->x2[c] = (float)x1;
pBQ->x1[c] = (float)x0;
pBQ->y2[c] = (float)y1;
pBQ->y1[c] = (float)y0;
}
}
} else {
......@@ -212,12 +236,12 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
c = cos(w);
a = s / (2*q);
bqConfig.a0 = (float)( 1 + a);
bqConfig.a1 = (float)(-2 * c);
bqConfig.a2 = (float)( 1 - a);
bqConfig.b0 = (float)((1 - c) / 2);
bqConfig.b1 = (float)( 1 - c);
bqConfig.b2 = (float)((1 - c) / 2);
bqConfig.a0 = (double)( 1 + a);
bqConfig.a1 = (double)(-2 * c);
bqConfig.a2 = (double)( 1 - a);
bqConfig.b0 = (double)((1 - c) / 2);
bqConfig.b1 = (double)( 1 - c);
bqConfig.b2 = (double)((1 - c) / 2);
bqConfig.format = pConfig->format;
bqConfig.channels = pConfig->channels;
......
......@@ -203,23 +203,25 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, ma
{
ma_uint64 frameCountOut;
ma_uint64 frameCountIn;
ma_uint64 iFrameOut;
ma_uint64 iFrameIn;
ma_uint64 iChannel;
float ratioInOut;
MA_ASSERT(pResampler != NULL);
MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFrameCountOut != NULL);
MA_ASSERT(pFramesIn != NULL);
MA_ASSERT(pFrameCountIn != NULL);
frameCountOut = *pFrameCountOut;
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) {
/* Pass in data from the input buffer. */
ma_uint64 iFrameOut;
ma_uint64 iFrameIn;
ma_uint64 iChannel;
ratioInOut = (float)pResampler->config.sampleRateIn / (float)pResampler->config.sampleRateOut;
iFrameOut = 0;
iFrameIn = 0;
......@@ -244,49 +246,63 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, ma
}
for (;;) {
float t0;
float t1;
float y;
if (iFrameOut >= frameCountOut || iFrameIn >= frameCountIn) {
break;
}
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
y = ma_mix_f32_fast(pResampler->state.linear.x0[iChannel], pResampler->state.linear.x1[iChannel], pResampler->state.linear.t);
}
t0 = pResampler->state.linear.t;
t1 = t0 + ratioInOut;
if (t1 >= 1) {
/* We can't interpolate if our interpolation factor (time relative to x0) is greater than 1. */
if (pResampler->state.linear.t > 1) {
/* Need to load the next input frame. */
iFrameIn += (ma_uint64)t1;
if (iFrameIn > 0) {
iFrameIn += (ma_uint64)pResampler->state.linear.t;
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) {
pResampler->state.linear.x0[iChannel] = pX[(iFrameIn-1)*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 {
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
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;
}
/* Here is where we set the number of frames that were consumed. */
*pFrameCountOut = iFrameOut;
*pFrameCountIn = iFrameIn;
} else {
/* Format not supported. */
return MA_INVALID_OPERATION;
}
} else {
/* Pass in zeroes. */
return MA_INVALID_OPERATION;
}
return MA_SUCCESS;
}
......@@ -299,6 +315,7 @@ static ma_result ma_resampler_process__read__linear_lpf(ma_resampler* pResampler
MA_ASSERT(pResampler != NULL);
MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFrameCountOut != NULL);
MA_ASSERT(pFramesIn != NULL);
MA_ASSERT(pFrameCountIn != NULL);
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*
MA_ASSERT(pResampler != 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) {
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)
{
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