[pulseaudio-discuss] [PATCH 5/6] lfe-filter: Add rewind support
David Henningsson
david.henningsson at canonical.com
Wed Jan 28 14:14:49 PST 2015
Store current filter state at every normal block process.
When a rewind happens, rewind back to the nearest saved state,
then calculate forward to the actual sample position.
Signed-off-by: David Henningsson <david.henningsson at canonical.com>
---
src/pulsecore/filter/lfe-filter.c | 98 +++++++++++++++++++++++++++++++++++++--
src/pulsecore/filter/lfe-filter.h | 5 +-
2 files changed, 97 insertions(+), 6 deletions(-)
diff --git a/src/pulsecore/filter/lfe-filter.c b/src/pulsecore/filter/lfe-filter.c
index 1597eca..ac76630 100644
--- a/src/pulsecore/filter/lfe-filter.c
+++ b/src/pulsecore/filter/lfe-filter.c
@@ -25,30 +25,54 @@
#include "lfe-filter.h"
#include <pulse/xmalloc.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/llist.h>
#include <pulsecore/filter/biquad.h>
#include <pulsecore/filter/crossover.h>
+struct saved_state {
+ PA_LLIST_FIELDS(struct saved_state);
+ pa_memchunk chunk;
+ int64_t index;
+ struct lr4 lr4[PA_CHANNELS_MAX];
+};
+
+PA_STATIC_FLIST_DECLARE(lfe_state, 0, pa_xfree);
+
// An LR4 filter, implemented as a chain of two LR2 filters.
struct pa_lfe_filter {
+ int64_t index;
+ PA_LLIST_HEAD(struct saved_state, saved);
float crossover;
pa_channel_map cm;
pa_sample_spec ss;
+ size_t maxrewind;
bool active;
struct lr4 lr4[PA_CHANNELS_MAX];
};
-pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq) {
+static void remove_state(pa_lfe_filter_t *f, struct saved_state *s) {
+ PA_LLIST_REMOVE(struct saved_state, f->saved, s);
+ pa_memblock_unref(s->chunk.memblock);
+ pa_xfree(s);
+}
+
+pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, size_t maxrewind) {
pa_lfe_filter_t *f = pa_xnew0(struct pa_lfe_filter, 1);
f->crossover = crossover_freq;
f->cm = *cm;
f->ss = *ss;
+ f->maxrewind = maxrewind;
pa_lfe_filter_update_rate(f, ss->rate);
return f;
}
void pa_lfe_filter_free(pa_lfe_filter_t *f) {
+ while (f->saved)
+ remove_state(f, f->saved);
+
pa_xfree(f);
}
@@ -56,11 +80,9 @@ void pa_lfe_filter_reset(pa_lfe_filter_t *f) {
pa_lfe_filter_update_rate(f, f->ss.rate);
}
-pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) {
+static void process_block(pa_lfe_filter_t *f, pa_memchunk *buf) {
int samples = buf->length / pa_sample_size_of_format(f->ss.format);
- if (!f->active)
- return buf;
if (f->ss.format == PA_SAMPLE_FLOAT32NE) {
int i;
float *data = pa_memblock_acquire_chunk(buf);
@@ -76,6 +98,32 @@ pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) {
pa_memblock_release(buf->memblock);
}
else pa_assert_not_reached();
+
+ f->index += samples;
+}
+
+pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) {
+ struct saved_state *s, *s2;
+
+ if (!f->active)
+ return buf;
+
+ // Remove old states (FIXME: we could do better than searching the entire array here?)
+ PA_LLIST_FOREACH_SAFE(s, s2, f->saved)
+ if (s->index + s->chunk.length + (int64_t) f->maxrewind < f->index)
+ remove_state(f, s);
+
+ // Insert our existing state into the flist
+ if ((s = pa_flist_pop(PA_STATIC_FLIST_GET(lfe_state))) == NULL)
+ s = pa_xnew(struct saved_state, 1);
+ PA_LLIST_INIT(struct saved_state, s);
+ s->index = f->index;
+ s->chunk = *buf;
+ pa_memblock_ref(buf->memblock);
+ memcpy(s->lr4, f->lr4, sizeof(struct lr4) * f->cm.channels);
+ PA_LLIST_PREPEND(struct saved_state, f->saved, s);
+
+ process_block(f, buf);
return buf;
}
@@ -83,6 +131,10 @@ void pa_lfe_filter_update_rate(pa_lfe_filter_t *f, uint32_t new_rate) {
int i;
float biquad_freq = f->crossover / (new_rate / 2);
+ while (f->saved)
+ remove_state(f, f->saved);
+
+ f->index = 0;
f->ss.rate = new_rate;
if (biquad_freq <= 0 || biquad_freq >= 1) {
pa_log_warn("Crossover frequency (%f) outside range for sample rate %d", f->crossover, new_rate);
@@ -95,3 +147,41 @@ void pa_lfe_filter_update_rate(pa_lfe_filter_t *f, uint32_t new_rate) {
f->active = true;
}
+
+void pa_lfe_filter_rewind(pa_lfe_filter_t *f, size_t amount) {
+ struct saved_state *i, *s = NULL;
+ size_t samples = amount / pa_sample_size_of_format(f->ss.format);
+ f->index -= samples;
+
+ // Find the closest saved position
+ PA_LLIST_FOREACH(i, f->saved) {
+ if (i->index > f->index)
+ continue;
+ if (s == NULL || i->index > s->index)
+ s = i;
+ }
+ if (s == NULL) {
+ pa_log_debug("Rewinding LFE filter %lu samples to position %lli. No saved state found", samples, (long long) f->index);
+ pa_lfe_filter_update_rate(f, f->ss.rate);
+ return;
+ }
+ pa_log_debug("Rewinding LFE filter %lu samples to position %lli. Found saved state at position %lli",
+ samples, (long long) f->index, (long long) s->index);
+ memcpy(f->lr4, s->lr4, sizeof(struct lr4) * f->cm.channels);
+
+ // now fast forward to the actual position
+ if (f->index > s->index) {
+ pa_memchunk x = s->chunk;
+ size_t left = f->index - s->index;
+ if (left > s->chunk.length) {
+ pa_log_error("Hole in stream, cannot fast forward LFE filter");
+ return;
+ }
+ pa_memblock_ref(s->chunk.memblock); // Make sure we get a copy - we want to keep our original unchanged
+ pa_memchunk_make_writable(&x, left);
+ f->index = s->index;
+ process_block(f, &x);
+ pa_memblock_unref(x.memblock);
+ pa_memblock_unref(s->chunk.memblock);
+ }
+}
diff --git a/src/pulsecore/filter/lfe-filter.h b/src/pulsecore/filter/lfe-filter.h
index 25db8a0..54d695b 100644
--- a/src/pulsecore/filter/lfe-filter.h
+++ b/src/pulsecore/filter/lfe-filter.h
@@ -25,13 +25,14 @@
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulsecore/memchunk.h>
-
+#include <pulsecore/memblockq.h>
typedef struct pa_lfe_filter pa_lfe_filter_t;
-pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq);
+pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, size_t maxrewind);
void pa_lfe_filter_free(pa_lfe_filter_t *);
void pa_lfe_filter_reset(pa_lfe_filter_t *);
+void pa_lfe_filter_rewind(pa_lfe_filter_t *, size_t amount);
pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *filter, pa_memchunk *buf);
void pa_lfe_filter_update_rate(pa_lfe_filter_t *, uint32_t new_rate);
--
1.9.1
More information about the pulseaudio-discuss
mailing list