lfe-filter: Enable LFE filter in the resampler

When enable-lfe-remixing is set, an LFE channel is present in the
resampler's destination channel map but not in the source channel map,
we insert a low-pass filter instead of just averaging the channels.
Other channels will get a high-pass filter.

In this patch, the crossover frequency is hardcoded to 120Hz (to be fixed
in later patches).

Note that in current state the LFE filter is
 - not very optimised
 - not rewind friendly (rewinding can cause audible artifacts)

Signed-off-by: David Henningsson <david.henningsson@canonical.com>
This commit is contained in:
David Henningsson 2015-03-24 10:29:13 +01:00
parent f3ebf6b667
commit 979f19a434
7 changed files with 266 additions and 6 deletions

View file

@ -40,7 +40,7 @@ struct ffmpeg_data { /* data specific to ffmpeg */
static int copy_init(pa_resampler *r);
static void setup_remap(const pa_resampler *r, pa_remap_t *m);
static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_filter_required);
static void free_remap(pa_remap_t *m);
static int (* const init_table[])(pa_resampler *r) = {
@ -324,6 +324,7 @@ pa_resampler* pa_resampler_new(
pa_resample_flags_t flags) {
pa_resampler *r = NULL;
bool lfe_filter_required = false;
pa_assert(pool);
pa_assert(a);
@ -412,7 +413,15 @@ pa_resampler* pa_resampler_new(
/* set up the remap structure */
if (r->map_required)
setup_remap(r, &r->remap);
setup_remap(r, &r->remap, &lfe_filter_required);
if (lfe_filter_required) {
pa_sample_spec wss = r->o_ss;
wss.format = r->work_format;
/* TODO: Temporary code that sets crossover freq to 120 Hz. This should be a parameter */
r->lfe_filter = pa_lfe_filter_new(&wss, &r->o_cm, 120.0f);
pa_log_debug(" lfe filter activated (LR4 type)");
}
/* initialize implementation */
if (init_table[method](r) < 0)
@ -434,6 +443,9 @@ void pa_resampler_free(pa_resampler *r) {
else
pa_xfree(r->impl.data);
if (r->lfe_filter)
pa_lfe_filter_free(r->lfe_filter);
if (r->to_work_format_buf.memblock)
pa_memblock_unref(r->to_work_format_buf.memblock);
if (r->remap_buf.memblock)
@ -472,6 +484,9 @@ void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
r->o_ss.rate = rate;
r->impl.update_rates(r);
if (r->lfe_filter)
pa_lfe_filter_update_rate(r->lfe_filter, rate);
}
size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
@ -556,6 +571,9 @@ void pa_resampler_reset(pa_resampler *r) {
if (r->impl.reset)
r->impl.reset(r);
if (r->lfe_filter)
pa_lfe_filter_reset(r->lfe_filter);
*r->have_leftover = false;
}
@ -756,7 +774,7 @@ static int front_rear_side(pa_channel_position_t p) {
return ON_OTHER;
}
static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_filter_required) {
unsigned oc, ic;
unsigned n_oc, n_ic;
bool ic_connected[PA_CHANNELS_MAX];
@ -765,6 +783,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
pa_assert(r);
pa_assert(m);
pa_assert(lfe_filter_required);
n_oc = r->o_ss.channels;
n_ic = r->i_ss.channels;
@ -777,6 +796,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
memset(m->map_table_i, 0, sizeof(m->map_table_i));
memset(ic_connected, 0, sizeof(ic_connected));
*lfe_filter_required = false;
if (r->flags & PA_RESAMPLER_NO_REMAP) {
for (oc = 0; oc < PA_MIN(n_ic, n_oc); oc++)
@ -888,6 +908,9 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
oc_connected = true;
ic_connected[ic] = true;
if (a == PA_CHANNEL_POSITION_MONO && on_lfe(b) && !(r->flags & PA_RESAMPLER_NO_LFE))
*lfe_filter_required = true;
}
else if (b == PA_CHANNEL_POSITION_MONO) {
m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
@ -970,6 +993,8 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
/* Please note that a channel connected to LFE doesn't
* really count as connected. */
*lfe_filter_required = true;
}
}
}
@ -1340,6 +1365,9 @@ void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out)
buf = remap_channels(r, buf);
}
if (r->lfe_filter)
buf = pa_lfe_filter_process(r->lfe_filter, buf);
if (buf->length) {
buf = convert_from_work_format(r, buf);
*out = *buf;