• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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