[pulseaudio-discuss] Biquad LR4 for module-lfe-lp

Justin Chudgar justin at justinzane.com
Sat Mar 30 08:37:13 PDT 2013

On Saturday, March 30, 2013 04:04:59 PM you wrote:
> 2013/3/30 Justin Chudgar <justin at justinzane.com>:
> > https://github.com/justinzane/pulseaudio.git
> > 
> > Alexander:
> > 
> > Now that I have a possibly working rewind implementation, Iwanted to make
> > sure that I understand your guidance on doing a correct LR4. Following is
> > what I currently think I need to do:
> > 
> > - split channels into 3 sets: lowpass, highpass, and allpass
> > 
> > - calculate the respective biquad coefficients using a Q=0.5, with one set
> > of coefficients for both stages of the lp/hp/ap cascades -- that is,
> > 
> >     [3][a0,a1,a2,b0,b1,b2]
> > 
> > - use two separate data buffersfor each channel, for the first and second
> > stages respectively -- that is,
> > 
> >     [numchannels][2][ [y0,y1,y2,w0,w1,w2]
> > 
> > Another question I have is whether float or double are more appropriate
> > for the actual calculations. Reducing my implementation from double to
> > float seems wise to me for speed and space reasons on 32bit platforms,
> > but I'm not sure that loss of precision in an IIR filter is acceptable.
> > 
> > If you review the actual code, I know it is not done to the style
> > standards. I am going to clean it up once I've made sure that it is
> > fundamentally correct.
> The description above does not match the logic behind LR4 filters.
> Since you are repeating the same misunderstanding over and over again,
> it would be useless to review your code now, and I am nearly out of
> ideas how to help you. So I will try the remaining two ideas: "use
> different words" and "use a picture".
> So, with different words and an example. Let's consider what you call
> a highpass channel. Let's say it is the rear-left channel. You filter
> it twice with a highpass filter and copy the filtered-twice result to
> the corresponding output channel. During this process, the high
> frequencies pass through, and the low frequencies are discarded. The
> "unwanted frequencies are discarded" part is what you did wrong. 

I disagree with this analysis for this use-case. If this implementation were 
in remix.c it would make sense. However, it works with fictitious, synthetic, 
duplicated, averaged, generally boooooooogus (can't find quite the right 
adjective) source data. This module does NOT do remixing. Though I would 
prefer this to be done in remix.c, many people have said that it should be in 
a module and I am simply trying to achieve what I want within the guidelines 
laid out by more experienced and smarter pulseaudio community members.

The only reason I began this effort was to get lowpass filtered output to my 
subwoofer when enabling lfe remixing in daemon.conf. 

I added the highpass to the center channel because it also makes sense to me 
to logically consider the center and lfe channels as a group of synthesized 
channels. The allpassing is just done to address the group delay, as I thought 
you had suggested. Everything but the lowpass filtering of the lfe channel is 
extraneous to me. And, since my wife and I and a couple of our guests have 
been listening to a previous commit all week, quite happily, I am sure that it 
mostly works as intended. 

You seem to have a design for a "crossover" that would be excellent for 
inclusion in remix.c. If that is something that might be considered for actual 
mainlining, I would consider trying to do it. For this module, I cannot see 
the relevance to my design goals.

> They should not be discarded. They should be extracted from the rear-left
> channel (and from all other channels) with a chain of two lowpass
> filters and moved to a channel capable of reproducing them - i.e., in
> our case, to the subwoofer. Conversely, unwanted high frequencies
> should be moved from the subwoofer, say, to the center channel or to
> all other channels.

My center channel speaker can reproduce 106Hz-20kHz. If I did not filter the 
low frequencies, they would be discarded by the little 3 inch drivers that are 
physically unable to reproduce them. Since the center channel source exists 
only due to remix.c averaging front-left and front-right in any case, nothing 
is discarded overall. 

The parallel is true for the subwoofer, it is fed an average of l+r, which 
includes frequecies that it simply cannot reproduce. These are discarded by 
the physical speaker in any case, so removing them with a filter simple allows 
the subwoofer to clearly reproduce the bass without muddying things up. Those 
"discarded" frequencies still exist in all the other channels.

The use-case for this module is primarily with mono/stereo sources that have 
been remixed by remix.c. It is easy enough, either with pacmd or something 
like Veromix (in KDE's Plasma) to move that BluRay playing in smplayer to the 
master sink and skip the filter. I am not yet familiar with how "properties" 
are used to guide the filter heuristics, but it may be possible to have this 
filter selectively applied for mono/stereo audio/video source streams while 
using the master sink directly with natively surround streams. In many ways, 
that would be an ideal outcome, to me.

> As for your use of allpass filters, I will verify separately whether
> it is a valid optimization.
> Regarding the biquad itself - the order of assignments in
> filter_biquad() is definitely wrong. Look, w1 always ends up being
> equal to w0, and y1 equal to y0.

That was intentional, [unless I munged something since I tested this :)] they 
are the historical data. They are simply a convenience since I did not want to 
do bounds checking on the *src_sample/*dst_sample pointers to the in and out 
memblock_q's because I do not understand the architecture that well.

It should be the case that w0 == *src_sample; w1 == *(src_sample - 
(sample_size * channels)); w2 == *(src_sample - 2 * (sample_size * channels)); 
and the same for the output samples.

> And now the promised picture:
> http://ac3filter.net/w/images/thumb/4/40/Bass_redirection_1.png/645px-Bass_r
> edirection_1.png . Each ^\ filter should be understood as a cascade of two
> Butterworth lowpass filters, and _/ is a cascade of two Butterworth
> highpass
> filters. As for their treatment of the subwoofer, I'd replace their
> allpass filter with a lowpass, and redirect (unwanted) high
> frequencies from the subwoofer to the center channel.

Since, by definition, 
	src_frame[lfe] ==
	src_frame[center] == 
	(0.5 * (src_frame[left] + src_frame[right])_, 
that seems to be exactly what I am accomplishing.
> --
> Alexander E. Patrakov

More information about the pulseaudio-discuss mailing list