Brutefir attenuates at lower sample rates

344 views
Skip to first unread message

Mervin Beng

unread,
Feb 2, 2011, 2:15:07 AM2/2/11
to BrutefirDRC
Olov, hope you don't mind - this issue is real, and probably needs its
own thread:

Olav posted that he observed a difference in volume between 88.2 or
96kHz material compared to 44.1 or 48kHz.

---- from Olav ------
I have noticed that 88.2 and 96 kHz files sound louder than 48 and
44.1 with brutefirdrc. There is no difference between 16 and 24bit.
To test I created a few 10 sec pink noise tracks of various sample
rates, both flac and wav, normalized to 0dBfs. I also converted a real
music track from 24/96 to 16/44.1 and observed the same. I did not
measure the level difference, but I'd guess 8dB corresponding to the
difference between 16 and 24bit.
Any one experience this? I am using analog out from my Touch at the
moment. Maybe this is a Touch issue.. I'll try the same with out
brutefir one of the first days.

Olav
---------------

I have reduced this problem to the following:

Using brutefir with a null eq filter on a 96kHz file, then on the
resampled version of the same file at say 48kHz, there is definitely a
difference in volume of the output files. 48Khz is audibly softer, and
this can be checked when examining the output files with something
like audacity.

When verified by some others we may need to bring this to Anders
Torger's attention.

Mervin

---

Filter used:

## DEFAULT GENERAL SETTINGS ##

float_bits: 64; # internal floating point precision
sampling_rate: 48000; # sampling rate in Hz of audio interfaces
filter_length: 8192,8; # length of filters
overflow_warnings: true; # echo warnings to stderr if overflow
occurs
show_progress: false; # echo filtering progress to stderr
max_dither_table_size: 0; # maximum size in bytes of precalculated
dither
allow_poll_mode: false; # allow use of input poll mode
modules_path: "."; # extra path where to find BruteFIR
modules
monitor_rate: false; # monitor sample rate
powersave: true; # pause filtering when input is zero
lock_memory: false; # try to lock memory if realtime prio is
set
convolver_config: "/etc/squeezeboxserver/BrutefirDrc/settings/wisdom";
# location o
f convolver config file

logic:
"eq" {
{
coeff: "equalizer";
bands: "ISO octave";
magnitude: 31.5/0.0, 63/0.0, 125/0.0, 250/0.0, 500/0.0, 1000/0.0, 2
000/0.0, 4000/0.0, 8000/0.0, 16000/0.0;
};
};

coeff "equalizer" {
filename: "dirac pulse";
shared_mem: true;
blocks: 4;
};

## INPUT DEFAULTS ##

input "l_in","r_in" {
device: "file" {path: "/dev/stdin";}; # module and parameters to get
audio
sample: "S24_LE"; # sample format
channels: 2/0,1; # number of open channels / which to use
delay: 0,0; # delay in samples for each channel
maxdelay: -1; # max delay for variable delays
mute: false,false; # mute active on startup for each channel
};

## OUTPUT DEFAULTS ##

output "l_out","r_out" {
device: "file" {path: "/dev/stdout";}; # module and parameters to put
audio
sample: "S24_LE"; # sample format
channels: 2/0,1; # number of open channels / which to use
delay: 0,0; # delay in samples for each channel
maxdelay: -1; # max delay for variable delays
mute: false,false; # mute active on startup for each channel
dither: true; # apply dither
};

# Add equalizer
filter "eq_l" {
from_inputs: "l_in";
to_outputs: "l_out";
process: -1; # process index to run in (-1 means auto)
coeff: "equalizer";
delay: 0; # predelay, in blocks
crossfade: false; # crossfade when coefficient is changed
};

filter "eq_r" {
from_inputs: "r_in";
to_outputs: "r_out";
process: -1; # process index to run in (-1 means auto)
coeff: "equalizer";
delay: 0; # predelay, in blocks
crossfade: false; # crossfade when coefficient is changed
};

Mervin Beng

unread,
Feb 2, 2011, 9:36:26 AM2/2/11
to BrutefirDRC
Ok, I think this is solved.

I've run brutefir(wrapper) on a 96k file, and a resampled 48k version
of it, using the same filter configs, etc. I gradually reduced
attenuation until just a little clipping occurs.

On the 96k config file, the attenuation needed was 9.0dB.On the 48k
config file, the attenuation at which a little clipping occurs was
3.0dB!

That's a major difference. Somehow lower sample rate files can make do
with approx. 6dB less input attenuation, all things else being the
same. I will have to re-explore the best attenuation settings for my
room filters, then carefully edit the filter configs so that maximum
output level without clipping is obtained with different source sample
rates.

Does anyone know of documentation on brutefir filters that explains
this?

Olav Sunde

unread,
Feb 2, 2011, 12:15:51 PM2/2/11
to BrutefirDRC


On Feb 2, 3:36 pm, Mervin Beng <merv...@mail.com> wrote:

> That's a major difference. Somehow lower sample rate files can make do
> with approx. 6dB less input attenuation, all things else being the
> same.

So it's 6dB, good work Mervin. I wonder why this occurs. Are you
certain this is related to brutefir alone? What about sox/flac and the
file conversions done in brutefirwrapper? If I understand correctly
your test used config files that bypassed resampling of filters as the
test file and filter had the same sample rate.
Maybe Toby can think of a test for this based on the performance
script he made the other day? Running files directly through brutefir
to /dev/null and looking at log output while changing attenuation.

Olav

Toby Dickenson

unread,
Feb 2, 2011, 12:17:32 PM2/2/11
to brute...@googlegroups.com
> Somehow lower sample rate files can make do
> with approx. 6dB less input attenuation

I have a hypothesis of what is going on here. A filter created at 96kHz is used
as-is when playing a 96kHz file, but has to be resampled (using sox) to 48kHz
when playing a 48kHz. That creates a filter with half as many samples. If
brutefir is performing a simple convolution then the magnitude of its output
will be reduced by half too, simply due to the smaller number of samples
(taps) in the filter. That is the 6dB.

That hypothesis fits the numbers. However I'm sure I remember reading that
brutefir scales its output by the total energy of its filter. If true, that
would nicely prevent this problem, although I remember the rationale for this
feature being that it would help avoid destroying tweeters if a random binary
file was accidentally used as a filter.

I cant find that in the brutefir documentation now, so maybe I imagined it.

I should be able to test this out tomorrow.....

> I've run brutefir(wrapper) on a 96k file, and a resampled 48k version
> of it, using the same filter configs, etc. I gradually reduced
> attenuation until just a little clipping occurs.

I think pink noise is a better method that checking for digital clipping.
Consider playing a file containing two tones, -10dBFS 30kHz and -16dBFS 15kHz -
you would see the same 6dB difference even if everything was working correctly.

--
Toby Dickenson

Toby Dickenson

unread,
Feb 2, 2011, 12:23:39 PM2/2/11
to brute...@googlegroups.com
On Wednesday 02 Feb 2011, Toby Dickenson wrote:
> That hypothesis fits the numbers. However I'm sure I remember reading that
> brutefir scales its output by the total energy of its filter.

I am thinking of Inguz.

Ok, I'm sure that is the problem. patch to follow....


--
Toby Dickenson

Mervin Beng

unread,
Feb 2, 2011, 5:16:33 PM2/2/11
to BrutefirDRC
Hi Olav,

On Feb 3, 1:15 am, Olav Sunde <ogsu...@gmail.com> wrote:
>
> So it's 6dB, good work Mervin. I wonder why this occurs. Are you
> certain this is related to brutefir alone? What about sox/flac and the
> file conversions done in brutefirwrapper? If I understand correctly
> your test used config files that bypassed resampling of filters as the
> test file and filter had the same sample rate.

My first suspicion was sox, so I did a run without brutefir, and the
output files were not attenuated at all.

In my tests, the filters were pre-built, but using exactly the sox
command that Toby uses in brutefirwrapper, so I'm pretty sure that the
filters are correct. I've also diff'd the filter config files just to
be sure, and listened to the output files.

> Maybe Toby can think of a test for this based on  the performance
> script he made the other day? Running files directly through brutefir
> to /dev/null and looking at log output while changing attenuation.
>
> Olav

Toby, would it be possible to upload a pink noise file which we can
use in common? It will be large, so perhaps in yousendit.com? It will
be helpful in providing us a common reference point.

I'm glad this group was set up. The discussions are already pointing
us towards (much) more efficient servers, and better calibrated
filters.

Olav Sunde

unread,
Feb 2, 2011, 5:47:31 PM2/2/11
to BrutefirDRC

First a correction. I write in the post Mervin quotes above that 8dB
is the difference betveen 16 and 24bits. This is nonsense of course.
8bits is the difference and each bit is 6dB.
So the difference we see here is 1bit or 6dB.
When I created the small pink noise files for testing I also made
176.4 and 192 files. These were downsampled correctly (I believe), but
playback has the same problems as 88.2 and 96kHz with the previous
version of brutefirwrapper. I do not yet own music files with this
resolution, but probably will soon. I hope Toby can find time look
into this.

Olav

Toby Dickenson

unread,
Feb 3, 2011, 2:32:37 PM2/3/11
to brute...@googlegroups.com
On Wednesday 02 Feb 2011, Toby Dickenson wrote:

> I have a hypothesis of what is going on here. A filter created at 96kHz is
> used as-is when playing a 96kHz file, but has to be resampled (using sox)
> to 48kHz when playing a 48kHz. That creates a filter with half as many
> samples. If brutefir is performing a simple convolution then the magnitude
> of its output will be reduced by half too, simply due to the smaller
> number of samples (taps) in the filter. That is the 6dB.

I have pushed an update to the git repository which addresses this effect by
patching the attenuation parameter in the brutefir configuration file,
proportional to the ratio of sample rates. This solves the gross 6dB, however
I am still not seeing an exact match in output magnitude when testing with a
1kHz pure tone. The error is much less than 1dB, so not a problem if it is
only a level-matching problem, but I fear that means there is still something
else wrong.

I wont have time for further investigation until the weekend. I would be
interesting to know if different sample rates still "sound right" with this
patch - ignoring minor level matching effects.

--
Toby Dickenson

Mervin Beng

unread,
Feb 3, 2011, 8:37:15 PM2/3/11
to BrutefirDRC
Hi Toby,

I looked at your latest code which includes adjustment to attenuation.
It seems to multiply the current attenuation by the ratio of
samplerates. My understanding it that the adjustment is independent of
current attenuation, so that:

For atten. at 44100 = 2dB, then atten. at 88200 = 2dB + 6dB for same
output.
For atten. at 96000 = 8dB, then atten. at 48000 = (8-6)dB.

Based on this, the code should be something like:

# attenuation *= float(samplerate)/baseline_samplerate # original
attenuation += 10*math.log10((1.0*samplerate/baseline_samplerate)**2)

For this I use dB = 10log10(P1/P2), and (P1/P2) = (samplerate/base)**2

Regards,
Mervin

Toby Dickenson

unread,
Feb 4, 2011, 3:12:29 AM2/4/11
to brute...@googlegroups.com
On Friday 04 Feb 2011, Mervin Beng wrote:

> Based on this, the code should be something like:
>
> # attenuation *= float(samplerate)/baseline_samplerate # original
> attenuation += 10*math.log10((1.0*samplerate/baseline_samplerate)**2)
>
> For this I use dB = 10log10(P1/P2), and (P1/P2) = (samplerate/base)**2

Yes, of course. Good catch.

(my first attempt at this used sox to apply the gain to the filter. sox's "vol"
effect uses linear gain, but brutefir is using dB)

> I am still not seeing an exact match in output magnitude when testing with a
> 1kHz pure tone

I was hoping to get an exact match after applying your fix, but it seems there
is still more to do.

My test script is:

#!/bin/bash
sox -n -c 2 -2 -r 48000 -t wavpcm a.wav synth 3 sine 1000 vol 0.5
Bin/brutefirwrapper not_a_mac /etc/squeezeboxser\
ver/BrutefirDrc/filters/Normal.txt < a.wav >fa.wav
sox -n -c 2 -2 -r 96000 -t wavpcm b.wav synth 3 sine 1000 vol 0.5
Bin/brutefirwrapper not_a_mac /etc/squeezeboxser\
ver/BrutefirDrc/filters/Normal.txt < b.wav >fb.wav

...then review the magnitude of fa and fb in audacity.


--
Toby Dickenson

Toby Dickenson

unread,
Feb 5, 2011, 7:37:08 PM2/5/11
to BrutefirDRC
> I was hoping to get an exact match after applying your fix, but it seems there
> is still more to do.

I have tracked down one remaining problem. We resample the filters
using sox, which changes the filter length, so it no longer fits in
the carefully tuned filter_length: 4096,16 configuration.

With hindsight the problem is obvious.

I suggest brutefirwrapper needs to rewrite that filter_length setting.
More code to follow tomorrow.....

Mervin Beng

unread,
Feb 6, 2011, 1:21:27 AM2/6/11
to BrutefirDRC
Perhaps the strategy can be to use a script to resample the sweep
using sox .... rate -v <samplerate>, then run lsconv and drc to
generate the filters required.

The filter config files for each filter samplerate may need to be
edited manually, because frequency cutoffs may need to be tuned for
higher samplerates. Possibly # samples for windws may also need to be
tuned.

This way the resulting filters generated will definitely have length
65536 (as specified in the .drc files), matching our brutefir settings
of 8192x8, 4096x16, etc.

Regards,
Mervin

On Feb 6, 8:37 am, Toby Dickenson <tobydicken...@googlemail.com>
wrote:

Olav Sunde

unread,
Feb 6, 2011, 2:26:57 AM2/6/11
to BrutefirDRC


On Feb 6, 7:21 am, Mervin Beng <merv...@mail.com> wrote:

>
> This way the resulting filters generated will definitely have length
> 65536 (as specified in the .drc files), matching our brutefir settings
> of 8192x8, 4096x16, etc.
>
> Regards,
> Mervin
>

What if you use a different filter_length than 65536? I am currently
using filter_length: 16384; i.e. no partitioning. Will a new scheme of
resampling handle this configuration correctly?


Olav

Toby Dickenson

unread,
Feb 10, 2011, 3:26:55 PM2/10/11
to brute...@googlegroups.com
On Sunday 06 Feb 2011, Toby Dickenson wrote:
> With hindsight the problem is obvious.
>
> I suggest brutefirwrapper needs to rewrite that filter_length setting.
> More code to follow tomorrow.....

Ive just pushed some code to git which achieves this. It checks the number of
samples in sox's resampled filter (based on file size) and increases the number
of partitions if necessary.

Unfortunately it is still not giving an exact match in my test script, so I am
now not entirely convinced this is going to be the right approach. For
resampling to a **lower** sample rate the result looks very poor. Increasing
the sample rate is causing, at least, a small magnitude error, but hopefully
nothing worse than that. Anyway, please try it out.

On Sunday 06 Feb 2011, Olav Sunde wrote:
> What if you use a different filter_length than 65536? I am currently
> using filter_length: 16384; i.e. no partitioning. Will a new scheme of
> resampling handle this configuration correctly?

The code, as it now stands, will rewrite this to
filter_length: 16384,2;

On Sunday 06 Feb 2011, Mervin Beng wrote:
> Perhaps the strategy can be to use a script to resample the sweep
> using sox .... rate -v <samplerate>, then run lsconv and drc to
> generate the filters required.

Yes, that sounds like a much better approach. Experiments in progress....

> This way the resulting filters generated will definitely have length
> 65536 (as specified in the .drc files), matching our brutefir settings

True, but I'm not sure thats desirable. Your filter impulse should be the same
duration (in seconds) independant of sample rate. Assuming there are sufficient
cpu cycles to process it.

--
Toby Dickenson

Mervin Beng

unread,
Feb 10, 2011, 6:30:01 PM2/10/11
to BrutefirDRC
Hi all,

> > Perhaps the strategy can be to use a script to resample the sweep
> > using sox .... rate -v <samplerate>, then run lsconv and drc to
> > generate the filters required.
>
> Yes, that sounds like a much better approach. Experiments in progress....
>
> > This way the resulting filters generated will definitely have length
> > 65536 (as specified in the .drc files), matching our brutefir settings
>
> True, but I'm not sure thats desirable. Your filter impulse should be the same
> duration (in seconds) independant of sample rate. Assuming there are sufficient
> cpu cycles to process it.

Yes, I understand that. I've created .drc files which have the window
lengths, etc adjusted according to sample rate. That's a bit of a
pain... To say time I use the same windows for 44.1 and 48, and 88.2
and 96, assuming ears are only gold-plated.

When I viewed the resulting filter files on Room EQ Qiz, the 96k
filter was greatly compressed (peaks and dips much shallower than
44.1k).

New factor: it appears PLMaxGain needs scaling with sample rate. When
I use a scaling of sqrt(samplerate/baserate), the filters are close,
but not identical. ALSO, I no longer seem to need to adjust
attenuation on the brutefir filter end (remember what started this
all?? ;) )

At this point, the filter impulse files look very close (within 1 dB
throughout the range), and I have pre-built filters which AFAIK are
according to how brutefirdrc was meant to be used. Attenuation is
consistent for all filter configs, although to get that. the .drc
gains needed to be adjusted.

Sound seems good, but my dac has been tweaked so much these few days
it's hard not to hear things that are not there.

Toby Dickenson

unread,
Feb 22, 2011, 7:27:40 PM2/22/11
to BrutefirDRC
> Unfortunately it is still not giving an exact match in my test script

I have finally tracked down one silly error in the code which patches
the brutefir configuration files. Very embarrasing, but the latest git
now passes my magnitude test. It sounds ok at 44 and 48kHz, and the
output wav looks ok in audacity at 96k.

If you are running the brutefirwrapper version which I pushed on 06
Feb then you really should upgrade to this version.


On Feb 10, 11:30 pm, Mervin Beng <merv...@mail.com> wrote:
> At this point, the filter impulse files look very close (within 1 dB
> throughout the range)

Can you post your drc configuration files?

Mervin Beng

unread,
Feb 23, 2011, 8:33:03 PM2/23/11
to BrutefirDRC

On Feb 23, 8:27 am, Toby Dickenson <tobydicken...@googlemail.com>
wrote:
> > Unfortunately it is still not giving an exact match in my test script
>
> I have finally tracked down one silly error in the code which patches
> the brutefir configuration files. Very embarrasing, but the latest git
> now passes my magnitude test. It sounds ok at 44 and 48kHz, and the
> output wav looks ok in audacity at 96k.

Your updated wrapper does a really nice job of patching the filters,
and I can see the filter lengths tuned to each sample rate, etc. Great
job. I've adapted it as a standalone filter generator, so that I can
build a set of filters at a go when re-measuring.

> If you are running the brutefirwrapper version which I pushed on 06
> Feb then you really should upgrade to this version.
>
> On Feb 10, 11:30 pm, Mervin Beng <merv...@mail.com> wrote:
>
> > At this point, the filter impulse files look very close (within 1 dB
> > throughout the range)
>
> Can you post your drc configuration files?

Here's the diff. As you can see, there are just changes to filter
length and window sizes. The only advantage I see with individually
built filters is that the target curves and other tweaking can be made
for higher sample rates, eg. drc to 30kHz, assuming mic and speaker
provide anything meaningful.

Question: when you use PSFilterType = L, is the output file longer
than the original (ie. output = preamble + filtered audio + tail)? I
was testing out PSFilterType = L, but found that the output is exactly
same length, which might indicate some truncation at the end, esp.
since there should not be truncation at the beginning. It was a quick
test though, and I might have done something incorrectly. As I listen
to a lot of classical music, gapless is very important.

$ diff my48000.drc my96000.drc
15c15
< BCSampleRate = 48000
---
> BCSampleRate = 96000
19,21c19,21
< BCInitWindow = 131072
< BCPreWindowLen = 1024
< BCPreWindowGap = 768
---
> BCInitWindow = 65536
> BCPreWindowLen = 2048 # 1024
> BCPreWindowGap = 1536 # 768
46c46
< BCDLEndFreq = 21000
---
> BCDLEndFreq = 23000
66,68c66,68
< MPWindowGap = 34
< MPLowerWindow = 35520 # 370 ms
< MPUpperWindow = 34 # 0.36 ms
---
> MPWindowGap = 38 # 34
> MPLowerWindow = 65536 # 35520 # 370 ms
> MPUpperWindow = 68 # 34 # 0.36 ms
70c70
< MPEndFreq = 21000
---
> MPEndFreq = 23000
78c78
< MPPFFinalWindow = 35520
---
> MPPFFinalWindow = 71040 # 35520
88c88
< DLEndFreq = 21000
---
> DLEndFreq = 23000
95,97c95,97
< EPWindowGap = 34
< EPLowerWindow = 1488 # 15.5 ms
< EPUpperWindow = 34 # 0.36 ms
---
> EPWindowGap = 68 # 34
> EPLowerWindow = 2976 # 1488 # 15.5 ms
> EPUpperWindow = 68 # 34 # 0.36 ms
99c99
< EPEndFreq = 21000
---
> EPEndFreq = 23000
108c108
< EPPFFinalWindow = 1488
---
> EPPFFinalWindow = 2976 # 1488
125,126c125,126
< ISPELowerWindow = 744
< ISPEUpperWindow = 558
---
> ISPELowerWindow = 1488 # 744
> ISPEUpperWindow = 1116 # 558
128c128
< ISPEEndFreq = 21000
---
> ISPEEndFreq = 23000
143c143
< PTReferenceWindow = 28800 # 300 ms
---
> PTReferenceWindow = 57600 # 28800 # 300 ms
147c147
< PTDLEndFreq = 21000
---
> PTDLEndFreq = 23000
164c164,165
< PLMaxGain = 1.96 # +5.11 dB Max
---
> #PLMaxGain = 1.8 # +5.11 dB Max
> PLMaxGain = 2.66 # +5.11 dB Max
178,180c179,181
< RTWindowGap = 34
< RTLowerWindow = 35520 # 370 ms
< RTUpperWindow = 34 # 0.36 ms
---
> RTWindowGap = 68 # 34
> RTLowerWindow = 71040 # 35520 # 370 ms
> RTUpperWindow = 68 # 34 # 0.36 ms
182c183
< RTEndFreq = 21000
---
> RTEndFreq = 23000
187c188
< RTOutWindow = 35520
---
> RTOutWindow = 71040 # 35520
210c211
< MSFilterDelay = 744
---
> MSFilterDelay = 1488 # 744

Toby Dickenson

unread,
Feb 24, 2011, 3:14:13 PM2/24/11
to brute...@googlegroups.com
On Thursday 24 Feb 2011, Mervin Beng wrote:

> Question: when you use PSFilterType = L, is the output file longer
> than the original (ie. output = preamble + filtered audio + tail)?

If I understand the question correctly:
The output of brutefirwrapper is always the same length as its input.
brutefirwrapper avoids gaps/truncations by mixing the reverberation tail from
the end of one track into the start of the next.


--
Toby Dickenson

Reply all
Reply to author
Forward
0 new messages