1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2014 David Henningsson, Canonical Ltd.
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include "lfe-filter.h"
25 #include <pulse/xmalloc.h>
26 #include <pulsecore/flist.h>
27 #include <pulsecore/llist.h>
28 #include <pulsecore/filter/biquad.h>
29 #include <pulsecore/filter/crossover.h>
30
31 struct saved_state {
32 PA_LLIST_FIELDS(struct saved_state);
33 pa_memchunk chunk;
34 int64_t index;
35 struct lr4 lr4[PA_CHANNELS_MAX];
36 };
37
38 PA_STATIC_FLIST_DECLARE(lfe_state, 0, pa_xfree);
39
40 /* An LR4 filter, implemented as a chain of two Butterworth filters.
41
42 Currently the channel map is fixed so that a highpass filter is applied to all
43 channels except for the LFE channel, where a lowpass filter is applied.
44 This works well for e g stereo to 2.1/5.1/7.1 scenarios, where the remap engine
45 has calculated the LFE channel to be the average of all source channels.
46 */
47
48 struct pa_lfe_filter {
49 int64_t index;
50 PA_LLIST_HEAD(struct saved_state, saved);
51 float crossover;
52 pa_channel_map cm;
53 pa_sample_spec ss;
54 size_t maxrewind;
55 bool active;
56 struct lr4 lr4[PA_CHANNELS_MAX];
57 };
58
remove_state(pa_lfe_filter_t * f,struct saved_state * s)59 static void remove_state(pa_lfe_filter_t *f, struct saved_state *s) {
60 PA_LLIST_REMOVE(struct saved_state, f->saved, s);
61 pa_memblock_unref(s->chunk.memblock);
62 pa_xfree(s);
63 }
64
pa_lfe_filter_new(const pa_sample_spec * ss,const pa_channel_map * cm,float crossover_freq,size_t maxrewind)65 pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, size_t maxrewind) {
66
67 pa_lfe_filter_t *f = pa_xnew0(struct pa_lfe_filter, 1);
68 f->crossover = crossover_freq;
69 f->cm = *cm;
70 f->ss = *ss;
71 f->maxrewind = maxrewind;
72 pa_lfe_filter_update_rate(f, ss->rate);
73 return f;
74 }
75
pa_lfe_filter_free(pa_lfe_filter_t * f)76 void pa_lfe_filter_free(pa_lfe_filter_t *f) {
77 while (f->saved)
78 remove_state(f, f->saved);
79
80 pa_xfree(f);
81 }
82
pa_lfe_filter_reset(pa_lfe_filter_t * f)83 void pa_lfe_filter_reset(pa_lfe_filter_t *f) {
84 pa_lfe_filter_update_rate(f, f->ss.rate);
85 }
86
process_block(pa_lfe_filter_t * f,pa_memchunk * buf,bool store_result)87 static void process_block(pa_lfe_filter_t *f, pa_memchunk *buf, bool store_result) {
88 int samples = buf->length / pa_frame_size(&f->ss);
89
90 void *garbage = store_result ? NULL : pa_xmalloc(buf->length);
91
92 if (f->ss.format == PA_SAMPLE_FLOAT32NE) {
93 int i;
94 float *data = pa_memblock_acquire_chunk(buf);
95 for (i = 0; i < f->cm.channels; i++)
96 lr4_process_float32(&f->lr4[i], samples, f->cm.channels, &data[i], garbage ? garbage : &data[i]);
97 pa_memblock_release(buf->memblock);
98 }
99 else if (f->ss.format == PA_SAMPLE_S16NE) {
100 int i;
101 short *data = pa_memblock_acquire_chunk(buf);
102 for (i = 0; i < f->cm.channels; i++)
103 lr4_process_s16(&f->lr4[i], samples, f->cm.channels, &data[i], garbage ? garbage : &data[i]);
104 pa_memblock_release(buf->memblock);
105 }
106 else pa_assert_not_reached();
107
108 pa_xfree(garbage);
109 f->index += samples;
110 }
111
pa_lfe_filter_process(pa_lfe_filter_t * f,pa_memchunk * buf)112 pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) {
113 pa_mempool *pool;
114 struct saved_state *s, *s2;
115 void *data;
116
117 if (!f->active || !buf->length)
118 return buf;
119
120 /* Remove old states (FIXME: we could do better than searching the entire array here?) */
121 PA_LLIST_FOREACH_SAFE(s, s2, f->saved)
122 if (s->index + (int64_t) (s->chunk.length / pa_frame_size(&f->ss) + f->maxrewind) < f->index)
123 remove_state(f, s);
124
125 /* Insert our existing state into the flist */
126 if ((s = pa_flist_pop(PA_STATIC_FLIST_GET(lfe_state))) == NULL)
127 s = pa_xnew(struct saved_state, 1);
128 PA_LLIST_INIT(struct saved_state, s);
129
130 /* TODO: This actually memcpys the entire chunk into a new allocation, because we need to retain the original
131 in case of rewinding. Investigate whether this can be avoided. */
132 data = pa_memblock_acquire_chunk(buf);
133 pool = pa_memblock_get_pool(buf->memblock);
134 s->chunk.memblock = pa_memblock_new_malloced(pool, pa_xmemdup(data, buf->length), buf->length);
135 s->chunk.length = buf->length;
136 s->chunk.index = 0;
137 pa_memblock_release(buf->memblock);
138 pa_mempool_unref(pool), pool = NULL;
139
140 s->index = f->index;
141 memcpy(s->lr4, f->lr4, sizeof(struct lr4) * f->cm.channels);
142 PA_LLIST_PREPEND(struct saved_state, f->saved, s);
143
144 process_block(f, buf, true);
145 return buf;
146 }
147
pa_lfe_filter_update_rate(pa_lfe_filter_t * f,uint32_t new_rate)148 void pa_lfe_filter_update_rate(pa_lfe_filter_t *f, uint32_t new_rate) {
149 int i;
150 float biquad_freq = f->crossover / (new_rate / 2);
151
152 while (f->saved)
153 remove_state(f, f->saved);
154
155 f->index = 0;
156 f->ss.rate = new_rate;
157 if (biquad_freq <= 0 || biquad_freq >= 1) {
158 pa_log_warn("Crossover frequency (%f) outside range for sample rate %d", f->crossover, new_rate);
159 f->active = false;
160 return;
161 }
162
163 for (i = 0; i < f->cm.channels; i++)
164 lr4_set(&f->lr4[i], f->cm.map[i] == PA_CHANNEL_POSITION_LFE ? BQ_LOWPASS : BQ_HIGHPASS, biquad_freq);
165
166 f->active = true;
167 }
168
pa_lfe_filter_rewind(pa_lfe_filter_t * f,size_t amount)169 void pa_lfe_filter_rewind(pa_lfe_filter_t *f, size_t amount) {
170 struct saved_state *i, *s = NULL;
171 size_t samples = amount / pa_frame_size(&f->ss);
172 f->index -= samples;
173
174 /* Find the closest saved position */
175 PA_LLIST_FOREACH(i, f->saved) {
176 if (i->index > f->index)
177 continue;
178 if (s == NULL || i->index > s->index)
179 s = i;
180 }
181 if (s == NULL) {
182 pa_log_debug("Rewinding LFE filter %zu samples to position %lli. No saved state found", samples, (long long) f->index);
183 pa_lfe_filter_update_rate(f, f->ss.rate);
184 return;
185 }
186 pa_log_debug("Rewinding LFE filter %zu samples to position %lli. Found saved state at position %lli",
187 samples, (long long) f->index, (long long) s->index);
188 memcpy(f->lr4, s->lr4, sizeof(struct lr4) * f->cm.channels);
189
190 /* now fast forward to the actual position */
191 if (f->index > s->index) {
192 pa_memchunk x = s->chunk;
193 x.length = (f->index - s->index) * pa_frame_size(&f->ss);
194 if (x.length > s->chunk.length) {
195 pa_log_error("Hole in stream, cannot fast forward LFE filter");
196 return;
197 }
198 f->index = s->index;
199 process_block(f, &x, false);
200 }
201 }
202