Commit 13a55128 authored by David Reid's avatar David Reid

Work in progress on the linear resampler.

parent 834a604d
...@@ -3,9 +3,8 @@ ...@@ -3,9 +3,8 @@
/* /*
TODO: 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. ma_biquad_reinit().
- Document how changing biquad constants requires reinitialization of the filter (due to issue above). ma_biquad_reinit(). - Document how ma_biquad_process_pcm_frames() and ma_lpf_process_pcm_frames() supports in-place filtering by passing in the same buffer for both the input and output.
- 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
...@@ -25,8 +24,6 @@ ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, dou ...@@ -25,8 +24,6 @@ ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, dou
typedef struct typedef struct
{ {
ma_biquad_config config; ma_biquad_config config;
ma_bool32 isPassthrough;
ma_uint32 prevFrameCount;
float x1[MA_MAX_CHANNELS]; /* x[n-1] */ float x1[MA_MAX_CHANNELS]; /* x[n-1] */
float x2[MA_MAX_CHANNELS]; /* x[n-2] */ float x2[MA_MAX_CHANNELS]; /* x[n-2] */
float y1[MA_MAX_CHANNELS]; /* y[n-1] */ float y1[MA_MAX_CHANNELS]; /* y[n-1] */
...@@ -35,7 +32,8 @@ typedef struct ...@@ -35,7 +32,8 @@ typedef struct
ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ); ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ);
ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
ma_uint32 ma_biquad_get_latency(ma_biquad* pBQ);
typedef struct typedef struct
...@@ -56,7 +54,8 @@ typedef struct ...@@ -56,7 +54,8 @@ typedef struct
ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF); ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF);
ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);
ma_result ma_lpf_process(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
ma_uint32 ma_lpf_get_latency(ma_lpf* pLPF);
#endif /* ma_lpf_h */ #endif /* ma_lpf_h */
...@@ -114,11 +113,6 @@ ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) ...@@ -114,11 +113,6 @@ ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
pBQ->config = *pConfig; pBQ->config = *pConfig;
if (pConfig->a0 == 1 && pConfig->a1 == 0 && pConfig->a2 == 0 &&
pConfig->b0 == 1 && pConfig->b1 == 0 && pConfig->b2 == 0) {
pBQ->isPassthrough = MA_TRUE;
}
/* Normalize. */ /* Normalize. */
pBQ->config.a1 /= pBQ->config.a0; pBQ->config.a1 /= pBQ->config.a0;
pBQ->config.a2 /= pBQ->config.a0; pBQ->config.a2 /= pBQ->config.a0;
...@@ -129,71 +123,84 @@ ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) ...@@ -129,71 +123,84 @@ ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
return MA_SUCCESS; return MA_SUCCESS;
} }
ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
{ {
ma_uint32 n;
ma_uint32 c; ma_uint32 c;
double a1 = pBQ->config.a1; const double a1 = pBQ->config.a1;
double a2 = pBQ->config.a2; const double a2 = pBQ->config.a2;
double b0 = pBQ->config.b0; const double b0 = pBQ->config.b0;
double b1 = pBQ->config.b1; const double b1 = pBQ->config.b1;
double b2 = pBQ->config.b2; const double b2 = pBQ->config.b2;
for (c = 0; c < pBQ->config.channels; c += 1) {
double x2 = pBQ->x2[c];
double x1 = pBQ->x1[c];
double x0 = pX[c];
double y2 = pBQ->y2[c];
double y1 = pBQ->y1[c];
double y0 = b0*x0 + b1*x1 + b2*x2 - a1*y1 - a2*y2;
pY[c] = (float)y0;
pBQ->x2[c] = (float)x1;
pBQ->x1[c] = (float)x0;
pBQ->y2[c] = (float)y1;
pBQ->y1[c] = (float)y0;
}
}
if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
return MA_INVALID_ARGS; {
ma_uint32 c;
const double a1 = pBQ->config.a1;
const double a2 = pBQ->config.a2;
const double b0 = pBQ->config.b0;
const double b1 = pBQ->config.b1;
const double b2 = pBQ->config.b2;
for (c = 0; c < pBQ->config.channels; c += 1) {
double x2 = pBQ->x2[c];
double x1 = pBQ->x1[c];
double x0 = pX[c] * 0.000030517578125; /* s16 -> f32 */
double y2 = pBQ->y2[c];
double y1 = pBQ->y1[c];
double y0 = b0*x0 + b1*x1 + b2*x2 - a1*y1 - a2*y2;
pY[c] = (ma_int16)(y0 * 32767.0); /* f32 -> s16 */
pBQ->x2[c] = (float)x1;
pBQ->x1[c] = (float)x0;
pBQ->y2[c] = (float)y1;
pBQ->y1[c] = (float)y0;
} }
}
/* Fast path for passthrough. */ ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
if (pBQ->isPassthrough) { {
if (pFramesOut != pFramesIn) { /* <-- The output buffer is allowed to be the same as the input buffer. */ ma_uint32 n;
ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pBQ->config.format, pBQ->config.channels));
}
return MA_SUCCESS; if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
return MA_INVALID_ARGS;
} }
/* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */
/* Currently only supporting f32. */ /* Currently only supporting f32. */
if (pBQ->config.format == ma_format_f32) { if (pBQ->config.format == ma_format_f32) {
float* pY = ( float*)pFramesOut; /* */ float* pY = ( float*)pFramesOut;
const float* pX = (const float*)pFramesIn; const float* pX = (const float*)pFramesIn;
for (n = 0; n < frameCount; n += 1) { for (n = 0; n < frameCount; n += 1) {
for (c = 0; c < pBQ->config.channels; c += 1) { ma_biquad_process_pcm_frame_f32(pBQ, pY + n*pBQ->config.channels, pX + n*pBQ->config.channels);
double x2 = pBQ->x2[c]; pY += pBQ->config.channels;
double x1 = pBQ->x1[c]; pX += pBQ->config.channels;
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 if (pBQ->config.format == ma_format_s16) { } else if (pBQ->config.format == ma_format_s16) {
/* */ ma_int16* pY = ( ma_int16*)pFramesOut; /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
const ma_int16* pX = (const ma_int16*)pFramesIn; const ma_int16* pX = (const ma_int16*)pFramesIn;
for (n = 0; n < frameCount; n += 1) { for (n = 0; n < frameCount; n += 1) {
for (c = 0; c < pBQ->config.channels; c += 1) { ma_biquad_process_pcm_frame_s16(pBQ, pY, pX);
double x2 = pBQ->x2[c]; pY += pBQ->config.channels;
double x1 = pBQ->x1[c]; pX += pBQ->config.channels;
double x0 = pX[n*pBQ->config.channels + c] * 0.000030517578125; /* s16 -> f32 */
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] = (ma_int16)(y0 * 32767.0); /* f32 -> s16 */
pBQ->x2[c] = (float)x1;
pBQ->x1[c] = (float)x0;
pBQ->y2[c] = (float)y1;
pBQ->y1[c] = (float)y0;
}
} }
} else { } else {
MA_ASSERT(MA_FALSE); MA_ASSERT(MA_FALSE);
...@@ -203,6 +210,15 @@ ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFrame ...@@ -203,6 +210,15 @@ ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFrame
return MA_SUCCESS; return MA_SUCCESS;
} }
ma_uint32 ma_biquad_get_latency(ma_biquad* pBQ)
{
if (pBQ == NULL) {
return 0;
}
return 2;
}
ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 cutoffFrequency) ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 cutoffFrequency)
{ {
...@@ -293,13 +309,32 @@ ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) ...@@ -293,13 +309,32 @@ ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
return MA_SUCCESS; return MA_SUCCESS;
} }
ma_result ma_lpf_process(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
{
ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
}
static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pFrameOut, const float* pFrameIn)
{
ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
}
ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
{ {
if (pLPF == NULL) { if (pLPF == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
return ma_biquad_process(&pLPF->bq, pFramesOut, pFramesIn, frameCount); return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
}
ma_uint32 ma_lpf_get_latency(ma_lpf* pLPF)
{
if (pLPF == NULL) {
return 0;
}
return ma_biquad_get_latency(&pLPF->bq);
} }
#endif #endif
\ No newline at end of file
This diff is collapsed.
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